WaitAll vs WhenAll

335

Apa perbedaan antara Task.WaitAll()dan Task.WhenAll()dari CTP Async? Bisakah Anda memberikan beberapa kode sampel untuk menggambarkan berbagai kasus penggunaan?

Yaron Levi
sumber

Jawaban:

504

Task.WaitAll memblokir utas saat ini sampai semuanya selesai.

Task.WhenAllmengembalikan tugas yang mewakili tindakan menunggu sampai semuanya selesai.

Itu berarti bahwa dari metode async, Anda dapat menggunakan:

await Task.WhenAll(tasks);

... yang berarti metode Anda akan berlanjut ketika semuanya selesai, tetapi Anda tidak akan mengikat utas untuk hanya berkeliaran sampai saat itu.

Jon Skeet
sumber
2
Setelah banyak membaca, Jelas bahwa async tidak ada hubungannya dengan utas blog.stephencleary.com/2013/11/there-is-no-thread.html
Vince Panuccio
7
@Vince: Saya pikir "tidak ada hubungannya dengan utas" adalah pernyataan yang berlebihan, dan penting untuk memahami bagaimana operasi async berinteraksi dengan utas.
Jon Skeet
6
@ KevinBui: Tidak, seharusnya tidak memblokirnya - ia akan menunggu tugas yang dikembalikan WhenAll, tapi itu tidak sama dengan memblokir utas.
Jon Skeet
1
@ JonSkeet Mungkin perbedaan yang tepat antara keduanya terlalu halus bagi saya. Bisakah Anda menunjukkan saya (dan mungkin, kita semua) pada beberapa referensi yang akan menjelaskan perbedaannya?
CatShoes
125
@CatShoes: Tidak juga - saya sudah menjelaskannya sebaik mungkin. Saya kira saya bisa memberikan analogi - itu seperti perbedaan antara memesan takeaway dan kemudian berdiri di pintu menunggu sampai tiba, vs memesan takeaway, melakukan hal-hal lain dan kemudian membuka pintu ketika kurir tiba ...
Jon Skeet
51

Sementara jawaban JonSkeet menjelaskan perbedaan dalam cara yang biasanya sangat baik ada perbedaan lain: penanganan pengecualian .

Task.WaitAllmelempar AggregateExceptionketika salah satu tugas melempar dan Anda dapat memeriksa semua pengecualian yang dilemparkan. The awaitdi await Task.WhenAllunwraps yang AggregateExceptiondan 'kembali' hanya pengecualian pertama.

Ketika program di bawah ini dijalankan dengan await Task.WhenAll(taskArray)output adalah sebagai berikut.

19/11/2016 12:18:37 AM: Task 1 started
19/11/2016 12:18:37 AM: Task 3 started
19/11/2016 12:18:37 AM: Task 2 started
Caught Exception in Main at 19/11/2016 12:18:40 AM: Task 1 throwing at 19/11/2016 12:18:38 AM
Done.

Ketika program di bawah ini dijalankan dengan Task.WaitAll(taskArray)output adalah sebagai berikut.

19/11/2016 12:19:29 AM: Task 1 started
19/11/2016 12:19:29 AM: Task 2 started
19/11/2016 12:19:29 AM: Task 3 started
Caught AggregateException in Main at 19/11/2016 12:19:32 AM: Task 1 throwing at 19/11/2016 12:19:30 AM
Caught AggregateException in Main at 19/11/2016 12:19:32 AM: Task 2 throwing at 19/11/2016 12:19:31 AM
Caught AggregateException in Main at 19/11/2016 12:19:32 AM: Task 3 throwing at 19/11/2016 12:19:32 AM
Done.

Program:

class MyAmazingProgram
{
    public class CustomException : Exception
    {
        public CustomException(String message) : base(message)
        { }
    }

    static void WaitAndThrow(int id, int waitInMs)
    {
        Console.WriteLine($"{DateTime.UtcNow}: Task {id} started");

        Thread.Sleep(waitInMs);
        throw new CustomException($"Task {id} throwing at {DateTime.UtcNow}");
    }

    static void Main(string[] args)
    {
        Task.Run(async () =>
        {
            await MyAmazingMethodAsync();
        }).Wait();

    }

    static async Task MyAmazingMethodAsync()
    {
        try
        {
            Task[] taskArray = { Task.Factory.StartNew(() => WaitAndThrow(1, 1000)),
                                 Task.Factory.StartNew(() => WaitAndThrow(2, 2000)),
                                 Task.Factory.StartNew(() => WaitAndThrow(3, 3000)) };

            Task.WaitAll(taskArray);
            //await Task.WhenAll(taskArray);
            Console.WriteLine("This isn't going to happen");
        }
        catch (AggregateException ex)
        {
            foreach (var inner in ex.InnerExceptions)
            {
                Console.WriteLine($"Caught AggregateException in Main at {DateTime.UtcNow}: " + inner.Message);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Caught Exception in Main at {DateTime.UtcNow}: " + ex.Message);
        }
        Console.WriteLine("Done.");
        Console.ReadLine();
    }
}
tymtam
sumber
13
perbedaan praktis terbesar adalah penanganan pengecualian. Betulkah? Karena itu sebenarnya bukan perbedaan praktis terbesar. Perbedaan praktis terbesar adalah satu async dan non blocking sedangkan yang lainnya blocking. Ini jauh lebih penting daripada bagaimana menangani pengecualian.
Liam
5
Terima kasih telah menunjukkan ini. Penjelasan ini bermanfaat dalam skenario yang sedang saya kerjakan. Mungkin bukan "perbedaan praktis terbesar", tapi jelas merupakan panggilan yang bagus.
Urk
Pengecualian penanganan menjadi perbedaan praktis terbesar mungkin lebih berlaku untuk perbandingan antara await t1; await t2; await t3;vsawait Task.WhenAll(t1,t2,t3);
frostshoxx
1
Tidakkah perilaku pengecualian ini bertentangan dengan dokumen di sini ( docs.microsoft.com/en-us/dotnet/api/… ) "Jika salah satu dari tugas yang diberikan selesai dalam keadaan yang salah, tugas yang dikembalikan juga akan selesai dalam keadaan yang salah , di mana pengecualiannya akan berisi kumpulan dari set pengecualian yang terbuka dari masing-masing tugas yang disediakan. "
Dasith Wijes
1
Saya menganggap ini sebagai artefak await, bukan perbedaan antara dua metode. Keduanya menyebarkan AggregateException, baik melempar secara langsung atau melalui properti ( Task.Exceptionproperti).
Theodor Zoulias
20

Sebagai contoh perbedaan - jika Anda memiliki tugas, lakukan sesuatu dengan utas UI (mis. Tugas yang mewakili animasi di Storyboard) jika Anda Task.WaitAll()maka utas UI diblokir dan UI tidak pernah diperbarui. jika Anda gunakan await Task.WhenAll()maka utas UI tidak diblokir, dan UI akan diperbarui.

J. Long
sumber
7

Apa yang mereka lakukan:

  • Secara internal keduanya melakukan hal yang sama.

Apa bedanya:

  • WaitAll adalah panggilan pemblokiran
  • WhenAll - not - code akan melanjutkan eksekusi

Gunakan kapan:

  • WaitAll ketika tidak dapat melanjutkan tanpa hasil
  • WhenAll ketika apa yang ingin diberitahukan, tidak diblokir
i100
sumber
1
@MartinRhodes Tapi bagaimana jika Anda tidak segera menunggunya, tetapi melanjutkan dengan pekerjaan lain dan kemudian menunggu? Anda tidak memiliki kemungkinan itu dengan WaitAllapa yang saya mengerti.
Jeppe
@ Jeppe Tidakkah Anda akan membuat panggilan berbeda Task.WaitAll setelah melakukan beberapa pekerjaan lain? Maksud saya, alih-alih memanggilnya segera setelah memulai tugas Anda.
PL