Dalam kode ini:
private async void button1_Click(object sender, EventArgs e) {
try {
await Task.WhenAll(DoLongThingAsyncEx1(), DoLongThingAsyncEx2());
}
catch (Exception ex) {
// Expect AggregateException, but got InvalidTimeZoneException
}
}
Task DoLongThingAsyncEx1() {
return Task.Run(() => { throw new InvalidTimeZoneException(); });
}
Task DoLongThingAsyncEx2() {
return Task.Run(() => { throw new InvalidOperation();});
}
Saya berharap WhenAll
untuk membuat dan melempar AggregateException
, karena setidaknya salah satu tugas yang ditunggu adalah pengecualian. Sebaliknya, saya mendapatkan kembali satu pengecualian yang dilemparkan oleh salah satu tugas.
Tidak WhenAll
selalu membuat AggregateException
?
.net
exception
asynchronous
tap
Michael Ray Lovett
sumber
sumber
AggregateException
. Jika Anda menggunakanTask.Wait
alih-alihawait
dalam contoh Anda, Anda akan menangkapAggregateException
Task.WhenAll
, dan saya jatuh ke dalam perangkap yang sama. Jadi saya sudah mencoba menjelaskan secara detail tentang perilaku ini.Jawaban:
Saya tidak begitu ingat di mana, tetapi saya membaca di suatu tempat bahwa dengan kata kunci async / await baru , kata kunci tersebut membuka
AggregateException
ke dalam pengecualian yang sebenarnya.Jadi, dalam blok tangkap, Anda mendapatkan pengecualian sebenarnya dan bukan gabungan. Ini membantu kami menulis kode yang lebih alami dan intuitif.
Ini juga diperlukan untuk memudahkan konversi kode yang ada menjadi menggunakan async / await di mana banyak kode mengharapkan pengecualian tertentu dan bukan pengecualian gabungan.
- Edit -
Mengerti:
An Async Primer oleh Bill Wagner
sumber
Saya tahu ini adalah pertanyaan yang sudah dijawab tetapi jawaban yang dipilih tidak benar - benar menyelesaikan masalah OP, jadi saya pikir saya akan memposting ini.
Solusi ini memberi Anda pengecualian agregat (yaitu semua pengecualian yang dilemparkan oleh berbagai tugas) dan tidak memblokir (alur kerja masih asinkron).
Kuncinya adalah menyimpan referensi ke tugas agregat sebelum Anda menunggunya, lalu Anda dapat mengakses properti Exception yang menyimpan AggregateException Anda (meskipun hanya satu tugas yang memberikan pengecualian).
Semoga masih bermanfaat. Saya tahu saya mengalami masalah ini hari ini.
sumber
throw task.Exception;
dalamcatch
blok? (Saya bingung melihat tangkapan kosong saat pengecualian benar-benar ditangani.)Task.IsCanceled
) tidak disebarkan dengan benar. Ini bisa diselesaikan dengan bantuan ekstensi seperti ini .Anda dapat melintasi semua tugas untuk melihat apakah ada lebih dari satu yang membuat pengecualian:
sumber
WhenAll
keluar setelah pengecualian pertama dan mengembalikannya. lihat: stackoverflow.com/questions/6123406/waitall-vs-whenallexceptions
berisi kedua pengecualian yang dilempar.await
penyebab pengecualian pertama dibuka, tetapi semua pengecualian memang masih tersedia melalui larik Tugas.Hanya berpikir saya akan memperluas jawaban @ Richiban untuk mengatakan bahwa Anda juga dapat menangani AggregateException di blok tangkap dengan mereferensikannya dari tugas. Misalnya:
sumber
Anda sedang memikirkan
Task.WaitAll
- itu melemparAggregateException
.WhenAll hanya menampilkan pengecualian pertama dari daftar pengecualian yang ditemuinya.
sumber
WhenAll
metode memilikiException
properti yangAggregateException
berisi semua pengecualian yang dilemparkan ke dalamnyaInnerExceptions
. Apa yang terjadi di sini adalahawait
membuang pengecualian dalam pertama, bukan pengecualianAggregateException
itu sendiri (seperti kata decyclone). Memanggil metode tugasWait
alih-alih menunggunya menyebabkan pengecualian asli dilempar.Banyak jawaban bagus di sini, tetapi saya masih ingin memposting kata-kata kasar saya karena saya baru saja menemukan masalah yang sama dan melakukan penelitian. Atau lompat ke versi TLDR di bawah.
Masalah
Menunggu
task
dikembalikan olehTask.WhenAll
hanya melempar pengecualian pertama dari yangAggregateException
disimpan ditask.Exception
, bahkan ketika beberapa tugas telah gagal.Dokumen saat ini untuk
Task.WhenAll
mengatakan:Yang benar, tetapi tidak mengatakan apa-apa tentang perilaku "membuka" yang disebutkan di atas saat tugas yang dikembalikan menunggu.
Saya kira, dokumen tidak menyebutkannya karena perilaku itu tidak spesifik
Task.WhenAll
.Ini hanyalah
Task.Exception
tipeAggregateException
dan untukawait
kelanjutan itu selalu dibuka sebagai pengecualian batin pertama, dengan desain. Ini bagus untuk kebanyakan kasus, karena biasanyaTask.Exception
hanya terdiri dari satu pengecualian internal. Tetapi pertimbangkan kode ini:Di sini, sebuah instance dari
AggregateException
akan dibuka ke pengecualian dalam pertamanyaInvalidOperationException
dengan cara yang persis sama seperti yang mungkin kita lakukan dengannyaTask.WhenAll
. Kami bisa saja gagal mengamatiDivideByZeroException
jika kami tidak melaluitask.Exception.InnerExceptions
langsung.Stephen Toub dari Microsoft menjelaskan alasan di balik perilaku ini dalam masalah GitHub terkait :
Satu hal penting lainnya yang perlu diperhatikan, perilaku membuka bungkus ini dangkal. Yaitu, itu hanya akan membuka pengecualian pertama dari
AggregateException.InnerExceptions
dan membiarkannya di sana, bahkan jika itu kebetulan merupakan contoh dari yang lainAggregateException
. Ini mungkin menambah lapisan kebingungan lainnya. Misalnya, mari kita ubahWhenAllWrong
seperti ini:Solusi (TLDR)
Jadi, kembali ke
await Task.WhenAll(...)
, yang saya pribadi inginkan adalah bisa:AggregateException
jika lebih dari satu pengecualian telah dilemparkan secara kolektif oleh satu atau lebih tugas;Task
satu - satunya untuk memeriksa nyaTask.Exception
;Task.IsCanceled
), sebagai sesuatu seperti ini tidak akan melakukannya:Task t = Task.WhenAll(...); try { await t; } catch { throw t.Exception; }
.Saya telah mengumpulkan ekstensi berikut untuk itu:
Sekarang, berikut ini bekerja seperti yang saya inginkan:
sumber
Ini berhasil untuk saya
sumber
WhenAll
tidak sama denganWhenAny
.await Task.WhenAny(tasks)
akan selesai segera setelah tugas selesai. Jadi, jika Anda memiliki satu tugas yang segera selesai dan berhasil dan tugas lainnya membutuhkan waktu beberapa detik sebelum memberikan pengecualian, ini akan segera dikembalikan tanpa kesalahan apa pun.Dalam kode Anda, pengecualian pertama dikembalikan menurut desain seperti yang dijelaskan di http://blogs.msdn.com/b/pfxteam/archive/2011/09/28/task-exception-handling-in-net-4-5. aspx
Sedangkan untuk pertanyaan Anda, Anda akan mendapatkan AggreateException jika Anda menulis kode seperti ini:
sumber