Ketika Anda menggunakan async
/ await
, tidak ada jaminan bahwa metode yang Anda panggil ketika Anda await FooAsync()
benar-benar akan berjalan secara tidak sinkron. Implementasi internal bebas untuk kembali menggunakan jalur yang sepenuhnya sinkron.
Jika Anda membuat API yang penting agar Anda tidak memblokir dan Anda menjalankan beberapa kode secara tidak sinkron, dan ada kemungkinan bahwa metode yang dipanggil akan berjalan secara sinkron (secara efektif memblokir), menggunakan await Task.Yield()
akan memaksa metode Anda menjadi tidak sinkron, dan kembali kontrol pada saat itu. Sisa kode akan dieksekusi di lain waktu (pada titik itu, masih dapat berjalan secara sinkron) pada konteks saat ini.
Ini juga dapat berguna jika Anda membuat metode asinkron yang memerlukan inisialisasi "lama berjalan", yaitu:
private async void button_Click(object sender, EventArgs e)
{
await Task.Yield(); // Make us async right away
var data = ExecuteFooOnUIThread(); // This will run on the UI thread at some point later
await UseDataAsync(data);
}
Tanpa Task.Yield()
panggilan, metode ini akan mengeksekusi secara sinkron hingga panggilan pertama await
.
await Task.Yield()
memaksa metode menjadi async, mengapa kita repot-repot menulis kode async "asli"? Bayangkan metode sinkronisasi yang berat. Untuk membuatnya async, cukup tambahkanasync
danawait Task.Yield()
pada awalnya dan secara ajaib, itu akan async? Itu akan seperti membungkus semua kode sinkronisasi ke dalamTask.Run()
dan membuat metode async palsu.Task.Run
untuk mengimplementasikannya,ExecuteFooOnUIThread
akan berjalan di kumpulan utas, bukan utas UI. Denganawait Task.Yield()
, Anda memaksanya untuk tidak sinkron dengan cara kode berikutnya masih berjalan pada konteks saat ini (hanya pada titik waktu kemudian). Ini bukan sesuatu yang biasa Anda lakukan, tetapi itu menyenangkan bahwa ada pilihan jika itu diperlukan untuk beberapa alasan aneh.ExecuteFooOnUIThread()
berjalan sangat lama, masih akan memblokir utas UI untuk waktu yang lama di beberapa titik dan membuat UI tidak responsif, apakah itu benar?Secara internal,
await Task.Yield()
cukup antri kelanjutan pada konteks sinkronisasi saat ini atau pada utas kumpulan acak, jikaSynchronizationContext.Current
adanull
.Ini secara efisien diimplementasikan sebagai penunggu kustom. Kode yang kurang efisien menghasilkan efek yang identik mungkin sesederhana ini:
Task.Yield()
dapat digunakan sebagai jalan pintas untuk beberapa perubahan alur eksekusi yang aneh. Sebagai contoh:Yang mengatakan, saya tidak bisa memikirkan kasus apa pun di mana
Task.Yield()
tidak dapat diganti denganTask.Factory.StartNew
tugas penjadwal yang tepatLihat juga:
"menunggu Task.Yield ()" dan alternatifnya
Task.Yield - penggunaan nyata?
sumber
var dialogTask = await showAsync();
?var dialogTask = await showAsync()
tidak akan dikompilasi karenaawait showAsync()
ekspresi tidak mengembalikan aTask
(tidak seperti itu tanpaawait
). Yang mengatakan, jika Anda melakukannyaawait showAsync()
, eksekusi setelah itu akan dilanjutkan hanya setelah dialog telah ditutup, itulah bedanya. Itu karenawindow.ShowDialog
API yang disinkronkan (meskipun masih memompa pesan). Dalam kode itu, saya ingin melanjutkan sementara dialog masih ditampilkan.Salah satu penggunaannya
Task.Yield()
adalah untuk mencegah stack overflow ketika melakukan rekursi async.Task.Yield()
mencegah kelanjutan sinkron. Namun, perlu diketahui bahwa ini dapat menyebabkan pengecualian OutOfMemory (seperti dicatat oleh Triynko). Rekursi tak berujung masih tidak aman dan Anda mungkin lebih baik menulis ulang rekursi sebagai sebuah loop.sumber
await Task.Delay(1)
sudah cukup untuk mencegahnya. (Aplikasi Konsol, .NET Core 3.1, C # 8)Task.Yield()
dapat digunakan dalam implementasi tiruan dari metode async.sumber