Jika saya tidak peduli dengan urutan penyelesaian tugas dan hanya ingin semuanya diselesaikan, haruskah saya tetap menggunakan await Task.WhenAll
alih-alih beberapa await
? misalnya, apakah di DoWork2
bawah metode yang disukai DoWork1
(dan mengapa?):
using System;
using System.Threading.Tasks;
namespace ConsoleApp
{
class Program
{
static async Task<string> DoTaskAsync(string name, int timeout)
{
var start = DateTime.Now;
Console.WriteLine("Enter {0}, {1}", name, timeout);
await Task.Delay(timeout);
Console.WriteLine("Exit {0}, {1}", name, (DateTime.Now - start).TotalMilliseconds);
return name;
}
static async Task DoWork1()
{
var t1 = DoTaskAsync("t1.1", 3000);
var t2 = DoTaskAsync("t1.2", 2000);
var t3 = DoTaskAsync("t1.3", 1000);
await t1; await t2; await t3;
Console.WriteLine("DoWork1 results: {0}", String.Join(", ", t1.Result, t2.Result, t3.Result));
}
static async Task DoWork2()
{
var t1 = DoTaskAsync("t2.1", 3000);
var t2 = DoTaskAsync("t2.2", 2000);
var t3 = DoTaskAsync("t2.3", 1000);
await Task.WhenAll(t1, t2, t3);
Console.WriteLine("DoWork2 results: {0}", String.Join(", ", t1.Result, t2.Result, t3.Result));
}
static void Main(string[] args)
{
Task.WhenAll(DoWork1(), DoWork2()).Wait();
}
}
}
await t1; await t2; ....; await tn
=> yang kedua selalu merupakan pilihan terbaik dalam kedua kasusJawaban:
Ya, gunakan
WhenAll
karena menyebarkan semua kesalahan sekaligus. Dengan beberapa menunggu, Anda kehilangan kesalahan jika salah satu dari yang sebelumnya menunggu lemparan.Perbedaan penting lainnya adalah bahwa
WhenAll
akan menunggu semua tugas selesai bahkan saat ada kegagalan (tugas yang salah atau dibatalkan). Menunggu secara manual secara berurutan akan menyebabkan konkurensi yang tidak terduga karena bagian dari program Anda yang ingin menunggu sebenarnya akan berlanjut lebih awal.Saya rasa itu juga membuat membaca kode lebih mudah karena semantik yang Anda inginkan langsung didokumentasikan dalam kode.
sumber
await
hasilnya.Pemahaman saya adalah bahwa alasan utama untuk memilih
Task.WhenAll
ke beberapaawait
s adalah kinerja / tugas "berputar": theDoWork1
metode melakukan sesuatu seperti ini:Sebaliknya,
DoWork2
lakukan ini:Apakah ini masalah yang cukup besar untuk kasus khusus Anda, tentu saja, "bergantung pada konteks" (maafkan permainan kata).
sumber
Metode asinkron diimplementasikan sebagai mesin negara. Dimungkinkan untuk menulis metode sehingga tidak dikompilasi ke dalam mesin-negara, ini sering disebut sebagai metode asinkron jalur cepat. Ini dapat diterapkan seperti ini:
Saat menggunakannya
Task.WhenAll
, dimungkinkan untuk mempertahankan kode jalur cepat ini sambil tetap memastikan pemanggil dapat menunggu hingga semua tugas diselesaikan, misalnya:sumber
(Penafian: Jawaban ini diambil / terinspirasi dari kursus Async TPL Ian Griffiths di Pluralsight )
Alasan lain untuk memilih WhenAll adalah penanganan Exception.
Misalkan Anda memiliki blok coba-tangkap pada metode DoWork Anda, dan anggaplah metode tersebut memanggil metode DoTask yang berbeda:
Dalam kasus ini, jika ketiga tugas menampilkan pengecualian, hanya tugas pertama yang akan ditangkap. Pengecualian selanjutnya akan hilang. Yaitu jika t2 dan t3 mengeluarkan eksepsi, hanya t2 yang akan ditangkap; dll. Pengecualian tugas berikutnya tidak akan teramati.
Dimana seperti di WhenAll - jika ada atau semua tugas gagal, tugas yang dihasilkan akan berisi semua pengecualian. Kata kunci await masih selalu memunculkan kembali pengecualian pertama. Jadi pengecualian lainnya masih belum teramati secara efektif. Salah satu cara untuk mengatasinya adalah dengan menambahkan lanjutan kosong setelah tugas WhenAll dan meletakkan menunggu di sana. Dengan cara ini jika tugas gagal, properti result akan menampilkan Pengecualian Gabungan penuh:
sumber
Jawaban lain untuk pertanyaan ini menawarkan alasan teknis mengapa
await Task.WhenAll(t1, t2, t3);
lebih disukai. Jawaban ini akan bertujuan untuk melihatnya dari sisi yang lebih lembut (yang disinggung @usr) sambil tetap sampai pada kesimpulan yang sama.await Task.WhenAll(t1, t2, t3);
adalah pendekatan yang lebih fungsional, karena mendeklarasikan maksud dan atom.Dengan
await t1; await t2; await t3;
, tidak ada yang mencegah rekan setimnya (atau mungkin bahkan masa depan Anda sendiri!) Dari menambahkan kode antara individuawait
pernyataan. Tentu, Anda telah mengompresnya menjadi satu baris untuk mencapai itu, tetapi itu tidak menyelesaikan masalah. Selain itu, umumnya merupakan bentuk yang buruk dalam pengaturan tim untuk menyertakan beberapa pernyataan pada baris kode tertentu, karena dapat membuat file sumber lebih sulit untuk dipindai oleh mata manusia.Sederhananya,
await Task.WhenAll(t1, t2, t3);
lebih mudah dipelihara, karena mengkomunikasikan maksud Anda dengan lebih jelas dan tidak terlalu rentan terhadap bug aneh yang dapat muncul dari pembaruan kode yang bermaksud baik, atau bahkan hanya menggabungkan kesalahan.sumber