Menunggu tugas yang sudah selesai sama seperti tugas. Hasil?

117

Saat ini saya membaca " Concurrency in C # Cookbook " oleh Stephen Cleary, dan saya memperhatikan teknik berikut:

var completedTask = await Task.WhenAny(downloadTask, timeoutTask);  
if (completedTask == timeoutTask)  
  return null;  
return await downloadTask;  

downloadTaskadalah panggilan ke httpclient.GetStringAsync, dan timeoutTasksedang dijalankan Task.Delay.

Jika tidak waktu tunggu, maka downloadTasksudah selesai. Mengapa perlu melakukan menunggu kedua daripada kembali downloadTask.Result, mengingat tugas sudah selesai?

julio.g
sumber
3
Ada sedikit konteks yang hilang di sini, dan kecuali orang sudah memiliki akses ke buku itu, Anda harus memasukkannya. Apa downloadTaskdan timeoutTask? Apa yang mereka lakukan?
Mike Perrenoud
7
Saya tidak melihat cek sebenarnya untuk penyelesaian yang berhasil di sini. Tugas tersebut bisa saja disalahkan, dan dalam hal ini perilakunya akan berbeda ( AggregateExceptiondengan Resultpengecualian pertama vs melalui ExceptionDispatchInfodengan await). Dibahas secara lebih rinci dalam Stephen Toub "Penanganan Pengecualian Tugas di .NET 4.5": blogs.msdn.com/b/pfxteam/archive/2011/09/28/… )
Kirill Shlenskiy
Anda harus membuat ini jawaban @klikliriklagu
Carsten
@MichaelPerrenoud Anda benar, terima kasih atas perhatiannya, saya akan mengedit pertanyaannya.
julio.g

Jawaban:

160

Sudah ada beberapa jawaban / komentar yang bagus di sini, tetapi hanya untuk berpadu ...

Ada dua alasan mengapa saya lebih memilih awaitlebih Result(atau Wait). Yang pertama adalah bahwa penanganan error berbeda; awaittidak membungkus pengecualian dalam AggregateException. Idealnya, kode asynchronous tidak harus berurusan AggregateExceptionsama sekali, kecuali jika secara khusus menginginkannya .

Alasan kedua sedikit lebih halus. Seperti yang saya jelaskan di blog saya (dan di buku), Result/ Waitdapat menyebabkan kebuntuan , dan dapat menyebabkan kebuntuan yang lebih halus lagi saat digunakan dalam suatu asyncmetode . Jadi, ketika saya membaca kode dan saya melihat Resultatau Wait, itu adalah tanda peringatan langsung. The Result/ Waithanya benar jika Anda benar-benar yakin bahwa tugas sudah selesai. Ini tidak hanya sulit dilihat secara sekilas (dalam kode dunia nyata), tetapi juga lebih rentan terhadap perubahan kode.

Itu bukan untuk mengatakan bahwa Result/ Waitharus tidak pernah digunakan. Saya mengikuti pedoman ini dalam kode saya sendiri:

  1. Kode asinkron dalam suatu aplikasi hanya dapat digunakan await.
  2. Kode utilitas asinkron (dalam pustaka) terkadang dapat menggunakan Result/ Waitjika kode benar-benar memanggilnya. Penggunaan seperti itu mungkin harus memiliki komentar.
  3. Kode tugas paralel dapat menggunakan Resultdan Wait.

Perhatikan bahwa (1) sejauh ini merupakan kasus umum, oleh karena itu saya cenderung menggunakan di awaitmana-mana dan memperlakukan kasus lain sebagai pengecualian dari aturan umum.

Stephen Cleary
sumber
Kami menemui jalan buntu menggunakan 'result' daripada 'await' dalam proyek kami. bagian yang kacau adalah tidak ada kesalahan kompilasi dan kode Anda menjadi tidak stabil setelah beberapa saat.
Ahmad Mousavi
@Stephen tolong jelaskan saya mengapa "Idealnya, kode asinkron tidak harus berurusan dengan AggregateException sama sekali, kecuali secara khusus menginginkannya"
vcRobe
4
@vcRobe Karena awaitmencegah AggregateExceptionpembungkusnya. AggregateExceptiondirancang untuk pemrograman paralel, bukan pemrograman asinkron.
Stephen Cleary
2
> "Tunggu hanya benar jika Anda benar-benar yakin bahwa tugas tersebut telah selesai." .... Lalu kenapa disebut Tunggu?
Ryan The Leach
4
@RyanTheLeach: Tujuan awal Waitadalah untuk bergabung dengan instance Dynamic Task Parallelism Task . Menggunakannya untuk menunggu Taskinstance asinkron berbahaya. Microsoft mempertimbangkan untuk memperkenalkan jenis "Janji" baru, tetapi memilih untuk menggunakan jenis yang sudah ada Task; tradeoff dari menggunakan kembali Taskjenis yang ada untuk tugas asinkron adalah Anda berakhir dengan beberapa API yang seharusnya tidak digunakan dalam kode asinkron.
Stephen Cleary
12

Ini masuk akal jika timeoutTaskmerupakan produk Task.Delay, yang saya percayai apa yang ada di dalam buku.

Task.WhenAnykembali Task<Task>, di mana tugas batin adalah salah satu yang Anda berikan sebagai argumen. Bisa ditulis ulang seperti ini:

Task<Task> anyTask = Task.WhenAny(downloadTask, timeoutTask);
await anyTask;
if (anyTask.Result == timeoutTask)  
  return null;  
return downloadTask.Result; 

Dalam kedua kasus tersebut, karena downloadTasktelah selesai, ada perbedaan yang sangat kecil antara return await downloadTaskdan return downloadTask.Result. Di AggregateExceptionsinilah yang terakhir akan membuang yang membungkus pengecualian asli apa pun, seperti yang ditunjukkan oleh @KirShlenskiy di komentar. Yang pertama hanya akan membuang pengecualian aslinya.

Dalam kedua kasus tersebut, di mana pun Anda menangani pengecualian, Anda harus memeriksa AggregateExceptiondan pengecualian dalamnya, untuk mengetahui penyebab kesalahan.

noseratio
sumber