Saya menemukan beberapa praktik terbaik untuk pemrograman asinkron menggunakan c # 's async
/ await
keywords (Saya baru mengenal c # 5.0).
Salah satu saran yang diberikan adalah sebagai berikut:
Stabilitas: Ketahui konteks sinkronisasi Anda
... Beberapa konteks sinkronisasi non-reentrant dan single-threaded. Ini berarti hanya satu unit pekerjaan yang dapat dijalankan dalam konteks pada waktu tertentu. Contohnya adalah utas Windows UI atau konteks permintaan ASP.NET. Dalam konteks sinkronisasi utas tunggal ini, Anda mudah menemui jalan buntu. Jika Anda mengeluarkan tugas dari konteks utas tunggal, lalu menunggu tugas itu dalam konteks, kode tunggu Anda mungkin memblokir tugas latar belakang.
public ActionResult ActionAsync()
{
// DEADLOCK: this blocks on the async task
var data = GetDataAsync().Result;
return View(data);
}
private async Task<string> GetDataAsync()
{
// a very simple async method
var result = await MyWebService.GetDataAsync();
return result.ToString();
}
Jika saya mencoba membedahnya sendiri, utas utama muncul ke utas baru MyWebService.GetDataAsync();
, tetapi karena utas utama menunggu di sana, utas utama menunggu hasilnya masuk GetDataAsync().Result
. Sementara itu, katakanlah data sudah siap. Mengapa utas utama tidak melanjutkan logika kelanjutannya dan mengembalikan hasil string dari GetDataAsync()
?
Adakah yang bisa menjelaskan kepada saya mengapa ada kebuntuan dalam contoh di atas? Saya benar-benar tidak tahu apa masalahnya ...
sumber
var data = GetDataAsync().Result;
adalah baris kode yang tidak boleh dilakukan dalam konteks yang tidak boleh Anda blokir (permintaan UI atau ASP.NET). Bahkan jika tidak mengalami kebuntuan, itu memblokir utas dalam waktu yang tidak pasti. Jadi pada dasarnya ini adalah contoh yang buruk. [Anda harus keluar dari utas UI sebelum menjalankan kode seperti itu, atau gunakan diawait
sana juga, seperti yang disarankan Toni.]Jawaban:
Lihat contoh ini , Stephen punya jawaban yang jelas untuk Anda:
Tautan lain yang harus Anda baca: Tunggu, dan UI, dan kebuntuan! Astaga!
sumber
GetDataAsync().Result;
akan berjalan saat tugas dikembalikan olehGetDataAsync()
selesai, sementara itu memblokir thread UIreturn result.ToString()
) diantrekan ke thread UI untuk dieksekusiGetDataAsync()
akan selesai saat antrian lanjutannya dijalankanJalan buntu!
Kebuntuan dapat dipecahkan dengan menyediakan alternatif untuk menghindari Fakta 1 atau Fakta 2.
var data = await GetDataAsync()
yang memungkinkan thread UI tetap berjalanvar data = Task.Run(GetDataAsync).Result
, yang akan memposting kelanjutan ke konteks sinkronisasi utas threadpool. Ini memungkinkan tugas yang dikembalikan olehGetDataAsync()
selesai.Ini dijelaskan dengan sangat baik dalam artikel oleh Stephen Toub , sekitar setengah jalan di mana dia menggunakan contoh
DelayAsync()
.sumber
var data = Task.Run(GetDataAsync).Result
itu baru bagi saya. Saya selalu berpikir bagian luar.Result
akan tersedia segera setelah menunggu pertamaGetDataAsync
dipukul, jadidata
akan selalu tersediadefault
. Menarik.Saya baru saja mengotak-atik masalah ini lagi dalam proyek ASP.NET MVC. Saat Anda ingin memanggil
async
metode dari aPartialView
, Anda tidak diizinkan membuatPartialView
async
. Anda akan mendapatkan pengecualian jika melakukannya.Anda dapat menggunakan solusi sederhana berikut dalam skenario di mana Anda ingin memanggil
async
metode dari metode sinkronisasi:SynchronizationContext
SynchronizationContext
Contoh:
public ActionResult DisplayUserInfo(string userName) { // trick to prevent deadlocks of calling async method // and waiting for on a sync UI thread. var syncContext = SynchronizationContext.Current; SynchronizationContext.SetSynchronizationContext(null); // this is the async call, wait for the result (!) var model = _asyncService.GetUserInfo(Username).Result; // restore the context SynchronizationContext.SetSynchronizationContext(syncContext); return PartialView("_UserInfo", model); }
sumber
Poin utama lainnya adalah Anda tidak boleh memblokir Tasks, dan menggunakan asinkron sepenuhnya untuk mencegah kebuntuan. Maka itu akan menjadi pemblokiran tidak sinkron semua asynchronous.
public async Task<ActionResult> ActionAsync() { var data = await GetDataAsync(); return View(data); } private async Task<string> GetDataAsync() { // a very simple async method var result = await MyWebService.GetDataAsync(); return result.ToString(); }
sumber
Solusi yang saya temukan adalah menggunakan
Join
metode ekstensi pada tugas sebelum meminta hasilnya.Kodenya terlihat seperti ini:
public ActionResult ActionAsync() { var task = GetDataAsync(); task.Join(); var data = task.Result; return View(data); }
Dimana metode bergabungnya adalah:
public static class TaskExtensions { public static void Join(this Task task) { var currentDispatcher = Dispatcher.CurrentDispatcher; while (!task.IsCompleted) { // Make the dispatcher allow this thread to work on other things currentDispatcher.Invoke(delegate { }, DispatcherPriority.SystemIdle); } } }
Saya tidak cukup masuk ke domain untuk melihat kekurangan dari solusi ini (jika ada)
sumber