Pemrograman asinkron tidak "tumbuh" melalui basis kode. Ini telah dibandingkan dengan virus zombie . Solusi terbaik adalah membiarkannya tumbuh, tetapi terkadang itu tidak mungkin.
Saya telah menulis beberapa jenis di pustaka Nito.AsyncEx saya untuk berurusan dengan basis kode asinkron parsial. Tidak ada solusi yang berfungsi di setiap situasi.
Solusi A
Jika Anda memiliki metode asinkron sederhana yang tidak perlu disinkronkan kembali ke konteksnya, maka Anda dapat menggunakan Task.WaitAndUnwrapException
:
var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();
Anda tidak ingin menggunakan Task.Wait
atau Task.Result
karena mereka memasukkan pengecualian AggregateException
.
Solusi ini hanya sesuai jika MyAsyncMethod
tidak disinkronkan kembali ke konteksnya. Dengan kata lain, setiap await
di MyAsyncMethod
harus berakhir dengan ConfigureAwait(false)
. Ini berarti tidak dapat memperbarui elemen UI apa pun atau mengakses konteks permintaan ASP.NET.
Solusi B
Jika MyAsyncMethod
memang perlu disinkronkan kembali ke konteksnya, maka Anda mungkin dapat menggunakan AsyncContext.RunTask
untuk memberikan konteks bersarang:
var result = AsyncContext.RunTask(MyAsyncMethod).Result;
* Pembaruan 4/14/2014: Dalam versi perpustakaan yang lebih baru, API adalah sebagai berikut:
var result = AsyncContext.Run(MyAsyncMethod);
(Tidak apa-apa untuk digunakan Task.Result
dalam contoh ini karena RunTask
akan menyebarkan Task
pengecualian).
Alasan Anda mungkin perlu AsyncContext.RunTask
bukannya Task.WaitAndUnwrapException
karena kebuntuan yang agak halus yang terjadi pada WinForms / WPF / SL / ASP.NET:
- Metode sinkron memanggil metode async, memperoleh a
Task
.
- Metode sinkron melakukan pemblokiran menunggu di Internet
Task
.
- The
async
Metode menggunakan await
tanpa ConfigureAwait
.
- Tidak
Task
dapat menyelesaikan dalam situasi ini karena hanya selesai ketika async
metode selesai; yang async
metode dapat tidak lengkap karena mencoba untuk menjadwalkan kelanjutan kepada SynchronizationContext
, dan WinForms / WPF / SL / ASP.NET tidak akan mengizinkan kelanjutan untuk menjalankan karena metode sinkron sudah berjalan dalam konteks itu.
Ini adalah salah satu alasan mengapa sebaiknya menggunakan sebanyak mungkin metode ConfigureAwait(false)
dalam setiap async
metode.
Solusi C
AsyncContext.RunTask
tidak akan berfungsi di setiap skenario. Misalnya, jika async
metode menunggu sesuatu yang memerlukan acara UI untuk menyelesaikan, maka Anda akan menemui jalan buntu bahkan dengan konteks bersarang. Jika demikian, Anda bisa memulai async
metode di kumpulan utas:
var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();
Namun, solusi ini membutuhkan MyAsyncMethod
yang akan bekerja dalam konteks thread pool. Jadi itu tidak dapat memperbarui elemen UI atau mengakses konteks permintaan ASP.NET. Dan dalam hal ini, Anda mungkin juga menambahkan ConfigureAwait(false)
untuk nya await
pernyataan, dan menggunakan solusi A.
Pembaruan, 2019-05-01: "Praktik terburuk" saat ini ada di artikel MSDN di sini .