Menggunakan CTP async dari Microsoft untuk .NET, apakah mungkin untuk menangkap pengecualian yang dilemparkan oleh metode async dalam metode panggilan?
public async void Foo()
{
var x = await DoSomethingAsync();
/* Handle the result, but sometimes an exception might be thrown.
For example, DoSomethingAsync gets data from the network
and the data is invalid... a ProtocolException might be thrown. */
}
public void DoFoo()
{
try
{
Foo();
}
catch (ProtocolException ex)
{
/* The exception will never be caught.
Instead when in debug mode, VS2010 will warn and continue.
The deployed the app will simply crash. */
}
}
Jadi pada dasarnya saya ingin pengecualian dari kode async untuk muncul ke kode panggilan saya jika itu mungkin sama sekali.
Jawaban:
Ini agak aneh untuk dibaca tetapi ya, pengecualian akan muncul hingga kode panggilan - tetapi hanya jika Anda
await
atauWait()
panggilan ituFoo
.Perhatikan bahwa menggunakan Tunggu () dapat menyebabkan aplikasi Anda diblokir, jika .Net memutuskan untuk menjalankan metode Anda secara sinkron.
Penjelasan ini http://www.interact-sw.co.uk/iangblog/2010/11/01/csharp5-async-exceptions cukup bagus - ini membahas langkah-langkah yang dilakukan oleh kompiler untuk mencapai keajaiban ini.
sumber
await
menelepon ke Foo, ketika Foo kembali batal?async void Foo()
.Type void is not awaitable
?Alasan pengecualian tidak ditangkap adalah karena metode Foo () memiliki tipe pengembalian kosong dan jadi ketika menunggu dipanggil, ia hanya kembali. Karena DoFoo () tidak menunggu penyelesaian Foo, pengendali pengecualian tidak dapat digunakan.
Ini membuka solusi yang lebih sederhana jika Anda dapat mengubah tanda tangan metode - ubah
Foo()
sehingga mengembalikan tipeTask
dan kemudianDoFoo()
bisaawait Foo()
, seperti dalam kode ini:sumber
Kode Anda tidak melakukan apa yang Anda pikirkan. Metode Async kembali segera setelah metode mulai menunggu hasil async. Wawasan untuk menggunakan pelacakan untuk menyelidiki bagaimana kode sebenarnya berperilaku.
Kode di bawah ini melakukan yang berikut:
Saat Anda mengamati jejaknya
Anda akan melihat bahwa metode Jalankan selesai pada utas 2820 sementara hanya satu utas anak telah selesai (2756). Jika Anda mencoba / menangkap metode menunggu Anda, Anda dapat "menangkap" pengecualian dengan cara biasa meskipun kode Anda dieksekusi di utas lain ketika tugas perhitungan telah selesai dan kontiuasi Anda dieksekusi.
Metode perhitungan melacak pengecualian yang dilemparkan secara otomatis karena saya memang menggunakan ApiChange.Api.dll dari alat ApiChange . Tracing dan Reflector sangat membantu untuk memahami apa yang sedang terjadi. Untuk menghilangkan threading, Anda dapat membuat GetAwaiter BeginAwait dan EndAwait versi Anda sendiri dan membungkus bukan tugas tetapi mis. Malas dan lacak di dalam metode ekstensi Anda sendiri. Maka Anda akan jauh lebih memahami apa yang dikompilasi dan apa yang dilakukan TPL.
Sekarang Anda melihat bahwa tidak ada cara untuk mencoba / menangkap pengecualian Anda kembali karena tidak ada bingkai tumpukan tersisa untuk pengecualian untuk merambat dari. Kode Anda mungkin melakukan sesuatu yang sama sekali berbeda setelah Anda melakukan operasi async. Mungkin memanggil Thread. Tidur atau bahkan mengakhiri. Selama masih ada satu foreground thread kiri aplikasi Anda akan dengan senang hati melanjutkan untuk melakukan tugas-tugas yang tidak sinkron.
Anda dapat menangani pengecualian di dalam metode async setelah operasi asinkron Anda selesai dan memanggil kembali ke utas UI. Cara yang disarankan untuk melakukan ini adalah dengan TaskScheduler.FromSynchronizationContext . Itu hanya berfungsi jika Anda memiliki utas UI dan tidak terlalu sibuk dengan hal-hal lain.
sumber
Pengecualian dapat ditangkap dalam fungsi async.
sumber
Penting juga untuk dicatat bahwa Anda akan kehilangan jejak tumpukan kronologis pengecualian jika Anda memiliki tipe pengembalian kosong pada metode async. Saya akan merekomendasikan mengembalikan Tugas sebagai berikut. Akan membuat debugging jauh lebih mudah.
sumber
return
pernyataan, kode ini berfungsi, karenaTask
"secara implisit" dikembalikan dengan menggunakanasync / await
.Blog ini menjelaskan masalah Anda Async Best Practices dengan rapi .
Intinya adalah Anda tidak boleh menggunakan void sebagai pengembalian untuk metode async, kecuali jika itu adalah event handler async, ini adalah praktik buruk karena tidak memungkinkan pengecualian ditangkap ;-).
Praktik terbaik adalah mengubah tipe pengembalian ke Tugas. Juga, coba kode async sepanjang jalan, membuat setiap panggilan metode async dan dipanggil dari metode async. Kecuali untuk metode Utama di konsol, yang tidak bisa async (sebelum C # 7.1).
Anda akan mengalami kebuntuan dengan aplikasi GUI dan ASP.NET jika Anda mengabaikan praktik terbaik ini. Kebuntuan terjadi karena aplikasi ini berjalan pada konteks yang hanya mengizinkan satu utas dan tidak akan menyerahkannya ke utas async. Ini berarti GUI menunggu secara sinkron untuk pengembalian, sementara metode async menunggu konteks: kebuntuan.
Perilaku ini tidak akan terjadi di aplikasi konsol, karena berjalan pada konteks dengan kumpulan utas. Metode async akan kembali pada utas lain yang akan dijadwalkan. Inilah sebabnya mengapa aplikasi konsol uji akan berfungsi, tetapi panggilan yang sama akan menemui jalan buntu di aplikasi lain ...
sumber