Dalam artikel MSDN ini , kode contoh berikut disediakan (sedikit diedit untuk singkatnya):
public async Task<ActionResult> Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Department department = await db.Departments.FindAsync(id);
if (department == null)
{
return HttpNotFound();
}
return View(department);
}
The FindAsync
Metode mengambil sebuah Department
objek dengan ID-nya, dan mengembalikan Task<Department>
. Kemudian departemen segera diperiksa untuk melihat apakah itu nol. Seperti yang saya pahami, meminta nilai Task dengan cara ini akan memblokir eksekusi kode hingga nilai dari metode yang ditunggu dikembalikan, secara efektif menjadikan ini panggilan sinkron.
Mengapa kamu melakukan ini? Bukankah lebih mudah untuk hanya memanggil metode sinkron Find(id)
, jika Anda akan segera memblokirnya?
c#
.net
asp.net-mvc
async
Robert Harvey
sumber
sumber
... else return null;
Maka Anda perlu memeriksa bahwa metode tersebut benar-benar menemukan departemen yang Anda minta.Jawaban:
Tidak terlalu.
Ketika Anda memanggil
await db.Departments.FindAsync(id)
tugas dikirim dan utas saat ini dikembalikan ke kolam untuk digunakan oleh operasi lain. Alur eksekusi diblokir (karena itu akan terlepas dari menggunakandepartment
segera setelah, jika saya memahami hal-hal dengan benar), tetapi utas itu sendiri bebas untuk digunakan oleh hal-hal lain saat Anda menunggu operasi selesai dari mesin (dan ditandai oleh suatu peristiwa atau port penyelesaian).Jika Anda menelepon
d.Departments.Find(id)
maka utasnya duduk di sana dan menunggu tanggapan, meskipun sebagian besar pemrosesan sedang dilakukan pada DB.Anda secara efektif membebaskan sumber daya CPU saat disk terikat.
sumber
await
dilakukan adalah menandatangani sisa metode sebagai kelanjutan dari utas yang sama (ada pengecualian; beberapa metode asink memutar benang mereka sendiri), atau menandatanganiasync
metode sebagai kelanjutan pada utas yang sama dan memungkinkan kode yang tersisa untuk dieksekusi (seperti yang Anda lihat, saya tidak jelas tentang caraasync
kerjanya). Apa yang Anda gambarkan terdengar seperti bentuk canggihThread.Sleep(untilReturnValueAvailable)
ConfigureAwait
iirc).await
diserukanpublic async Task<ActionResult> Details(int? id)
. Jika tidak, panggilan asli hanya akan diblokir, menunggu untukdepartment == null
diselesaikan.await ...
"kembali"FindAsync
panggilan telah selesai. Itu yang menunggu. Ini disebut menunggu karena itu membuat kode Anda menunggu. (Tetapi perhatikan bahwa ini tidak sama dengan membuat utas saat ini menunggu hal-hal)Saya benar-benar benci karena tidak ada contoh yang menunjukkan bagaimana mungkin menunggu beberapa baris sebelum menunggu tugas. Pertimbangkan ini.
Ini adalah jenis kode yang didukung contoh, dan Anda benar. Ada sedikit akal dalam hal ini. Itu membebaskan utas utama untuk melakukan hal-hal lain, seperti menanggapi input UI, tetapi kekuatan sebenarnya dari async / menunggu adalah saya dapat dengan mudah terus melakukan hal-hal lain sementara saya sedang menunggu tugas yang berpotensi berjalan lama untuk diselesaikan. Kode di atas akan "memblokir" dan menunggu untuk mengeksekusi garis cetak sampai kita mendapatkan Foo & Bar. Tidak perlu menunggu. Kita bisa memprosesnya sambil menunggu.
Sekarang, dengan kode yang ditulis ulang, kita tidak berhenti dan menunggu nilai-nilai kita sampai kita harus. Saya selalu mencari peluang seperti ini. Menjadi pintar saat kita menunggu dapat menyebabkan peningkatan kinerja yang signifikan. Kami punya beberapa core hari ini, mungkin lebih baik menggunakannya.
sumber
await
dan membiarkan utas melakukan hal yang sama sekali berbeda.Jadi ada lagi yang terjadi di balik layar di sini. Async / Await adalah gula sintaksis. Pertama-tama lihat tanda tangan dari fungsi FindAsync. Ini mengembalikan Tugas. Sudah Anda melihat keajaiban kata kunci, itu membuka kotak Tugas itu menjadi Departemen.
Fungsi panggilan tidak menghalangi. Apa yang terjadi adalah bahwa penugasan ke departemen dan segala sesuatu yang mengikuti kata kunci tunggu akan dimasukkan ke dalam penutupan dan untuk semua maksud dan tujuan diteruskan ke metode Task.ContinueWith (fungsi FindAsync secara otomatis dieksekusi pada utas yang berbeda).
Tentu saja ada lebih banyak yang terjadi di balik layar karena operasi diatur kembali ke utas asli (sehingga Anda tidak perlu lagi khawatir tentang sinkronisasi dengan UI saat melakukan operasi latar belakang) dan dalam kasus fungsi panggilan menjadi Async ( dan dipanggil secara tidak sinkron) hal yang sama terjadi di stack.
Jadi yang terjadi adalah Anda mendapatkan keajaiban operasi Async, tanpa jebakan.
sumber
Tidak, itu tidak segera kembali. Menunggu membuat panggilan metode asinkron. Ketika FindAsync dipanggil, metode Detail kembali dengan tugas yang belum selesai. Ketika FindAsync selesai, itu akan mengembalikan hasilnya ke variabel departemen dan melanjutkan sisa metode Detail.
sumber
async
await
umumnya tidak membuat utas baru, dan bahkan jika itu terjadi, Anda masih perlu menunggu nilai departemen itu untuk mengetahui apakah itu nol.public async Task<ActionResult>
juga harus diubahawait
.await
tidak boleh dicampur dengan.Wait()
atau.Result
, karena ini dapat menyebabkan kebuntuan. Rantai async / menunggu biasanya berakhir pada fungsi denganasync void
tanda tangan, yang sebagian besar digunakan untuk penangan acara, atau untuk fungsi yang dipanggil langsung oleh elemen UI.Saya suka berpikir "async" seperti kontrak, kontrak yang mengatakan "saya bisa menjalankan ini secara tidak sinkron jika Anda membutuhkannya, tetapi Anda dapat memanggil saya seperti fungsi sinkron lainnya juga".
Artinya, satu pengembang membuat fungsi dan beberapa keputusan desain membuat mereka membuat / menandai banyak fungsi sebagai "async". Penelepon / konsumen fungsi memiliki kebebasan untuk menggunakannya saat mereka memutuskan. Seperti yang Anda katakan, Anda dapat memanggil menunggu sebelum panggilan fungsi dan menunggu, dengan cara ini Anda memperlakukannya seperti fungsi sinkron, tetapi jika Anda ingin, Anda dapat memanggilnya tanpa menunggu sebagai
dan setelah, katakanlah, 10 baris ke bawah fungsi yang Anda panggil
karenanya memperlakukannya sebagai fungsi asinkron.
Terserah kamu.
sumber
"Jika Anda akan segera memblokirnya," jawabnya adalah "Ya". Hanya ketika Anda membutuhkan respons cepat, tunggu / async masuk akal. Misalnya utas UI datang ke metode yang benar-benar asinkron, utas UI akan kembali dan terus mendengarkan klik tombol, sementara kode di bawah "menunggu" akan dikecualikan oleh utas lain dan akhirnya mendapatkan hasilnya.
sumber