Saya sedang menulis aplikasi WinForms yang mentransfer data ke perangkat kelas HID USB. Aplikasi saya menggunakan pustaka HID Generic v6.0 yang luar biasa yang dapat ditemukan di sini . Singkatnya, ketika saya perlu menulis data ke perangkat, ini adalah kode yang dipanggil:
private async void RequestToSendOutputReport(List<byte[]> byteArrays)
{
foreach (byte[] b in byteArrays)
{
while (condition)
{
// we'll typically execute this code many times until the condition is no longer met
Task t = SendOutputReportViaInterruptTransfer();
await t;
}
// read some data from device; we need to wait for this to return
RequestToGetInputReport();
}
}
Ketika kode saya keluar dari loop sementara, saya perlu membaca beberapa data dari perangkat. Namun, perangkat tidak dapat segera merespons sehingga saya harus menunggu panggilan ini kembali sebelum saya melanjutkan. Karena saat ini ada, RequestToGetInputReport () dinyatakan seperti ini:
private async void RequestToGetInputReport()
{
// lots of code prior to this
int bytesRead = await GetInputReportViaInterruptTransfer();
}
Untuk apa nilainya, deklarasi untuk GetInputReportViaInterruptTransfer () terlihat seperti ini:
internal async Task<int> GetInputReportViaInterruptTransfer()
Sayangnya, saya tidak terlalu terbiasa dengan cara kerja teknologi async / menunggu baru di .NET 4.5. Saya melakukan sedikit bacaan sebelumnya tentang kata kunci yang menunggu dan yang memberi saya kesan bahwa panggilan untuk GetInputReportViaInterruptTransfer () di dalam RequestToGetInputReport () akan menunggu (dan mungkin ya?) Tetapi sepertinya panggilan ke RequestToGetInputReport () itu sendiri sedang menunggu karena saya tampaknya akan segera memasuki loop sementara?
Adakah yang bisa mengklarifikasi perilaku yang saya lihat?
sumber
void
menjadiTask
seperti yang Anda katakan.GetAwaiter().GetResult()
Task
eksekusi dari metode - sehinggareturn
nilai ditempatkanTask.Result
, dan pengecualian ditempatkan padaTask.Exception
. Denganvoid
, kompiler tidak memiliki tempat untuk menempatkan pengecualian, jadi mereka hanya diangkat kembali pada utas thread pool.Hal yang paling penting untuk mengetahui tentang
async
danawait
adalah bahwaawait
tidak menunggu panggilan terkait untuk lengkap. Apa yangawait
dilakukan adalah mengembalikan hasil operasi segera dan serempak jika operasi telah selesai atau, jika belum, untuk menjadwalkan kelanjutan untuk menjalankan sisaasync
metode dan kemudian mengembalikan kontrol ke penelepon. Ketika operasi asinkron selesai, penyelesaian yang dijadwalkan kemudian akan dijalankan.Jawaban untuk pertanyaan spesifik dalam judul pertanyaan Anda adalah untuk memblokir
async
nilai pengembalian metode (yang harus bertipeTask
atauTask<T>
) dengan memanggilWait
metode yang sesuai :Dalam cuplikan kode ini,
CallGetFooAsyncAndWaitOnResult
adalah pembungkus sinkron di sekitar metode asinkronGetFooAsync
. Namun, pola ini harus dihindari untuk sebagian besar karena akan memblokir seluruh thread pool selama operasi asinkron. Ini merupakan penggunaan yang tidak efisien dari berbagai mekanisme asinkron yang diekspos oleh API yang berupaya keras untuk menyediakannya.Jawaban di "menunggu" tidak menunggu penyelesaian panggilan memiliki beberapa, lebih rinci, penjelasan kata kunci ini.
Sementara itu, panduan @Stephen Cleary tentang penangguhan
async void
. Penjelasan bagus lainnya untuk alasannya dapat ditemukan di http://www.tonicodes.net/blog/why-you-should-almost-never-write-void-asynchronous-methods/ dan https://jaylee.org/archive/ 2012/07/08 / c-tajam-async-tips-dan-trik-bagian-2-async-void.htmlsumber
await
sebagai "menunggu asinkron" - yaitu, ia memblokir metode (jika perlu) tetapi bukan utasnya . Jadi masuk akal untuk berbicara tentangRequestToSendOutputReport
"menunggu"RequestToGetInputReport
meskipun itu bukan menunggu pemblokiran .Solusi terbaik untuk menunggu AsynMethod hingga menyelesaikan tugasnya
sumber
Berikut ini solusinya menggunakan bendera:
sumber
cukup tunggu () untuk menunggu sampai tugas selesai
GetInputReportViaInterruptTransfer().Wait();
sumber
Sebenarnya saya menemukan ini lebih bermanfaat untuk fungsi yang mengembalikan IAsyncAction.
sumber
Cuplikan berikut menunjukkan cara untuk memastikan metode yang ditunggu selesai sebelum kembali ke pemanggil. NAMUN, saya tidak akan mengatakan itu latihan yang baik. Harap edit jawaban saya dengan penjelasan jika Anda berpikir sebaliknya.
sumber
await
+Console.WriteLine
adalah dengan menjadiTask
, yang memberikan kontrol di antara keduanya. jadi 'solusi' Anda pada akhirnya akan menghasilkanTask<T>
, yang tidak mengatasi masalah. MelakukanTask.Wait
wasiat akan benar-benar berhenti memproses (dengan kemungkinan kebuntuan dll). Dengan kata lain,await
tidak benar-benar menunggu, itu hanya menggabungkan dua bagian yang dapat dieksekusi secara tidak serempak menjadi satuTask
(yang dapat ditonton atau ditunggu seseorang)