Pendahuluan : Saya mencari penjelasan, bukan hanya solusi. Saya sudah tahu solusinya.
Meskipun telah menghabiskan beberapa hari mempelajari artikel MSDN tentang Asynchronous Pattern (TAP) berbasis Tugas, async dan menunggu, saya masih agak bingung tentang beberapa detail yang lebih baik.
Saya sedang menulis logger untuk Aplikasi Windows Store, dan saya ingin mendukung logging asinkron dan sinkron. Metode asinkron mengikuti TAP, yang sinkron harus menyembunyikan semua ini, dan terlihat dan berfungsi seperti metode biasa.
Ini adalah metode inti pencatatan asinkron:
private async Task WriteToLogAsync(string text)
{
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile file = await folder.CreateFileAsync("log.log",
CreationCollisionOption.OpenIfExists);
await FileIO.AppendTextAsync(file, text,
Windows.Storage.Streams.UnicodeEncoding.Utf8);
}
Sekarang metode sinkron yang sesuai ...
Versi 1 :
private void WriteToLog(string text)
{
Task task = WriteToLogAsync(text);
task.Wait();
}
Ini terlihat benar, tetapi tidak berhasil. Seluruh program membeku selamanya.
Versi 2 :
Hmm .. Mungkin tugasnya belum dimulai?
private void WriteToLog(string text)
{
Task task = WriteToLogAsync(text);
task.Start();
task.Wait();
}
Ini melempar InvalidOperationException: Start may not be called on a promise-style task.
Versi 3:
Hmm .. Task.RunSynchronously
kedengarannya menjanjikan.
private void WriteToLog(string text)
{
Task task = WriteToLogAsync(text);
task.RunSynchronously();
}
Ini melempar InvalidOperationException: RunSynchronously may not be called on a task not bound to a delegate, such as the task returned from an asynchronous method.
Versi 4 (solusinya):
private void WriteToLog(string text)
{
var task = Task.Run(async () => { await WriteToLogAsync(text); });
task.Wait();
}
Ini bekerja. Jadi, 2 dan 3 adalah alat yang salah. Tapi 1? Apa yang salah dengan 1 dan apa bedanya dengan 4? Apa yang membuat saya membeku? Apakah ada masalah dengan objek tugas? Apakah ada jalan buntu yang tidak jelas?
sumber
Jawaban:
Bagian
await
dalam metode asinkron Anda sedang mencoba untuk kembali ke utas UI.Karena utas UI sedang sibuk menunggu seluruh tugas selesai, Anda menemui jalan buntu.
Memindahkan panggilan async untuk
Task.Run()
menyelesaikan masalah.Karena panggilan async sekarang berjalan pada utas pool thread, ia tidak mencoba untuk kembali ke utas UI, dan karenanya semuanya berfungsi.
Atau, Anda bisa menelepon
StartAsTask().ConfigureAwait(false)
sebelum menunggu operasi bagian dalam untuk membuatnya kembali ke kolam thread daripada thread UI, menghindari kebuntuan sepenuhnya.sumber
ConfigureAwait(false)
adalah solusi yang tepat dalam kasus ini. Karena tidak perlu memanggil callback dalam konteks yang ditangkap, seharusnya tidak. Menjadi metode API itu harus menanganinya secara internal, daripada memaksa semua penelepon untuk keluar dari konteks UI.Microsoft.Bcl.Async
.Memanggil
async
kode dari kode sinkron bisa sangat sulit.Saya menjelaskan alasan lengkap untuk kebuntuan ini di blog saya . Singkatnya, ada "konteks" yang disimpan secara default di awal masing-masing
await
dan digunakan untuk melanjutkan metode.Jadi jika ini disebut dalam konteks UI, ketika
await
selesai,async
metode mencoba memasukkan kembali konteks itu untuk melanjutkan eksekusi. Sayangnya, kode yang menggunakanWait
(atauResult
) akan memblokir utas dalam konteks itu, sehinggaasync
metode ini tidak dapat diselesaikan.Pedoman untuk menghindari ini adalah:
ConfigureAwait(continueOnCapturedContext: false)
sebanyak mungkin. Ini memungkinkanasync
metode Anda untuk terus mengeksekusi tanpa harus memasukkan kembali konteksnya.async
semuanya. Gunakanawait
sebagai gantiResult
atauWait
.Jika metode Anda secara alami tidak sinkron, maka Anda (mungkin) tidak boleh mengekspos pembungkus sinkron .
sumber
async
bagaimana saya melakukan ini dan mencegah kebakaran dan melupakan situasi.await
didukung dalamcatch
blok pada VS2015. Jika Anda menggunakan versi yang lebih lama, Anda dapat menetapkan pengecualian ke variabel lokal dan melakukanawait
setelah blok tangkap .Inilah yang saya lakukan
bekerja dengan baik dan tidak memblokir utas UI
sumber
Dengan konteks sinkronisasi kustom kecil, fungsi sinkronisasi dapat menunggu penyelesaian fungsi async, tanpa membuat jalan buntu. Berikut adalah contoh kecil untuk aplikasi WinForms.
sumber