Saya tidak begitu mengerti perbedaan antara Task.Wait
dan await
.
Saya memiliki sesuatu yang mirip dengan fungsi-fungsi berikut dalam layanan ASP.NET WebAPI:
public class TestController : ApiController
{
public static async Task<string> Foo()
{
await Task.Delay(1).ConfigureAwait(false);
return "";
}
public async static Task<string> Bar()
{
return await Foo();
}
public async static Task<string> Ros()
{
return await Bar();
}
// GET api/test
public IEnumerable<string> Get()
{
Task.WaitAll(Enumerable.Range(0, 10).Select(x => Ros()).ToArray());
return new string[] { "value1", "value2" }; // This will never execute
}
}
Dimana Get
akan menemui jalan buntu.
Apa yang bisa menyebabkan ini? Mengapa ini tidak menyebabkan masalah ketika saya menggunakan menunggu menunggu alih-alih await Task.Delay
?
Task.Delay(1).Wait()
yang cukup baik.Task.Delay(1).Wait()
pada dasarnya sama persis denganThread.Sleep(1000)
. Dalam kode produksi aktual, ini jarang sesuai.WaitAll
menyebabkan kebuntuan. Lihat tautan ke blog saya di jawaban saya untuk lebih jelasnya. Anda harus menggunakannyaawait Task.WhenAll
sebagai gantinya.ConfigureAwait(false)
sebuah tunggal panggilan untukBar
atauRos
tidak akan deadlock, tetapi karena Anda memiliki enumerable yang menciptakan lebih dari satu dan kemudian menunggu semua orang, bar pertama akan kebuntuan kedua. Jika Andaawait Task.WhenAll
alih-alih menunggu semua tugas, sehingga Anda tidak memblokir konteks ASP, Anda akan melihat metode kembali secara normal..ConfigureAwait(false)
semua jalan di atas pohon sampai Anda memblokir, dengan cara itu tidak ada yang pernah mencoba untuk kembali ke konteks utama; itu akan berhasil. Pilihan lain adalah memutar konteks sinkronisasi dalam. Link . Jika Anda menempatkanTask.WhenAll
dalamAsyncPump.Run
secara efektif akan memblokir di seluruh hal tanpa Anda perlu untukConfigureAwait
di mana saja, tapi itu mungkin solusi yang terlalu kompleks.Jawaban:
Wait
danawait
- walaupun serupa secara konseptual - sebenarnya sangat berbeda.Wait
akan secara sinkron memblokir sampai tugas selesai. Jadi utas saat ini benar-benar diblokir menunggu tugas selesai. Sebagai aturan umum, Anda harus menggunakan "async
sepenuhnya"; yaitu, jangan memblokirasync
kode. Di blog saya, saya masuk ke detail bagaimana memblokir kode asinkron menyebabkan kebuntuan .await
akan asinkron menunggu sampai tugas selesai. Ini berarti metode saat ini "dijeda" (statusnya ditangkap) dan metode mengembalikan tugas yang tidak lengkap ke pemanggilnya. Kemudian, ketikaawait
ekspresi selesai, sisa metode dijadwalkan sebagai kelanjutan.Anda juga menyebutkan "blok kooperatif", yang saya anggap maksud Anda tugas yang sedang Anda
Wait
jalankan dapat dijalankan pada utas menunggu. Ada situasi di mana ini bisa terjadi, tetapi ini adalah optimasi. Ada banyak situasi di mana itu tidak bisa terjadi, seperti jika tugas adalah untuk penjadwal lain, atau jika sudah dimulai atau jika itu adalah tugas non-kode (seperti dalam contoh kode Anda:Wait
tidak dapat menjalankanDelay
tugas inline karena tidak ada kode untuk itu).Anda mungkin menemukan
async
/await
intro saya bermanfaat.sumber
Wait
bekerja dengan baikawait
deadlock.Wait
memblokir utas, dan tidak dapat digunakan untuk hal lain.await
kode tersebut. Entah itu, atau kebuntuan tidak terkait dengan salah satu dan Anda salah mendiagnosis masalah.SynchronizationContext
, jadi memblokir dalam permintaan ASP.NET Core tidak ada lagi deadlock.Berdasarkan apa yang saya baca dari berbagai sumber:
Sebuah
await
ekspresi tidak memblokir thread di mana ia mengeksekusi. Alih-alih, itu menyebabkan kompiler untuk mendaftar sisaasync
metode sebagai kelanjutan dari tugas yang ditunggu. Kontrol kemudian kembali ke pemanggilasync
metode. Ketika tugas selesai, ia memanggil kelanjutannya, dan eksekusiasync
metode dilanjutkan di tempat yang ditinggalkannya.Untuk menunggu satu
task
sampai selesai, Anda dapat memanggilTask.Wait
metodenya. Panggilan keWait
metode memblokir utas panggilan sampai instance kelas tunggal telah menyelesaikan eksekusi.Wait()
Metode parameterless digunakan untuk menunggu tanpa syarat sampai tugas selesai. Tugas mensimulasikan pekerjaan dengan memanggilThread.Sleep
metode untuk tidur selama dua detik.Artikel ini juga merupakan bacaan yang bagus.
sumber
Beberapa fakta penting tidak diberikan dalam jawaban lain:
"async await" lebih kompleks pada level CIL dan karenanya menghabiskan waktu memori dan CPU.
Tugas apa pun dapat dibatalkan jika waktu tunggu tidak dapat diterima.
Dalam kasus "async menanti" kami tidak memiliki penangan untuk tugas seperti itu untuk membatalkan atau memonitornya.
Menggunakan Task lebih fleksibel daripada "menunggu async".
Fungsi sinkronisasi apa pun dapat dengan dibungkus oleh async.
Saya tidak mengerti mengapa saya harus hidup dengan duplikasi kode untuk sinkronisasi dan metode async atau menggunakan peretasan.
sumber