Saya mencoba memahami async menunggu dalam bentuk yang paling sederhana. Saya ingin membuat metode yang sangat sederhana yang menambahkan dua angka demi contoh ini, memang, tidak ada waktu pemrosesan sama sekali, itu hanya masalah merumuskan contoh di sini.
Contoh 1
private async Task DoWork1Async()
{
int result = 1 + 2;
}
Contoh 2
private async Task DoWork2Async()
{
Task.Run( () =>
{
int result = 1 + 2;
});
}
Jika saya menunggu DoWork1Async()
kode akan berjalan secara sinkron atau tidak sinkron?
Apakah saya perlu membungkus kode sinkronisasi dengan Task.Run
agar metode ini bisa ditunggu DAN tidak sinkron agar tidak memblokir utas UI?
Saya mencoba mencari tahu apakah metode saya adalah Task
atau mengembalikan Task<T>
apakah saya harus membungkus kode dengan Task.Run
agar asinkron.
Pertanyaan bodoh saya yakin tetapi saya melihat contoh di internet di mana orang menunggu kode yang tidak memiliki async di dalam dan tidak dibungkus dengan a Task.Run
atau StartNew
.
sumber
Jawaban:
Pertama, mari kita jelaskan beberapa terminologi: "asynchronous" (
async
) berarti bahwa ia dapat menghasilkan kontrol kembali ke utas panggilan sebelum dimulai. Dalam suatuasync
metode, titik "hasil" itu adalahawait
ekspresi.Ini sangat berbeda dari istilah "asinkron", seperti (mis) yang digunakan oleh dokumentasi MSDN selama bertahun-tahun yang berarti "dieksekusi di utas latar belakang".
Lebih lanjut membingungkan masalah,
async
sangat berbeda dari "ditunggu"; ada beberapaasync
metode yang jenis pengembaliannya tidak menunggu, dan banyak metode mengembalikan jenis yang ditunggu tidakasync
.Cukup tentang apa yang bukan mereka ; inilah yang mereka adalah :
async
kunci memungkinkan metode asinkron (yaitu, memungkinkanawait
ekspresi).async
metode dapat kembaliTask
,,Task<T>
atau (jika Anda harus)void
.Task
danTask<T>
.Jadi, jika kami merumuskan kembali pertanyaan Anda ke "bagaimana saya bisa menjalankan operasi pada utas latar belakang dengan cara yang bisa ditunggu", jawabannya adalah menggunakan
Task.Run
:(Tetapi pola ini adalah pendekatan yang buruk; lihat di bawah).
Tetapi jika pertanyaan Anda adalah "bagaimana cara membuat
async
metode yang dapat menghasilkan kembali ke pemanggilnya alih-alih memblokir", jawabannya adalah untuk mendeklarasikan metodeasync
dan gunakanawait
untuk poin "menghasilkan":Jadi, pola dasar segala sesuatu adalah memiliki
async
kode bergantung pada "yang dapat ditunggu" dalamawait
ekspresi. "Barang yang bisa ditunggu-tunggu" ini bisa berupaasync
metode lain atau hanya metode biasa untuk mengembalikan barang yang bisa ditunggu. Metode biasa kembaliTask
/Task<T>
dapat menggunakanTask.Run
untuk mengeksekusi kode di thread latar belakang, atau (lebih umum) mereka dapat menggunakanTaskCompletionSource<T>
atau salah satu cara pintas nya (TaskFactory.FromAsync
,Task.FromResult
, dll). Saya tidak merekomendasikan membungkus seluruh metode diTask.Run
; Metode sinkron harus memiliki tanda tangan sinkron, dan harus diserahkan kepada konsumen apakah harus dibungkus denganTask.Run
:Saya memiliki
async
/await
intro di blog saya; pada akhirnya adalah beberapa sumber daya tindak lanjut yang baik. Dokumen MSDNasync
juga luar biasa bagus.sumber
async
metode harus kembaliTask
,Task<T>
atauvoid
.Task
danTask<T>
bisa ditunggu;void
tidak.async void
tanda tangan metode akan dikompilasi, itu hanya ide yang cukup mengerikan ketika Anda kehilangan pointer ke tugas async Andavoid
tidak bisa ditunggu.Task.Run
(sepertiDoWorkAsync
dalam jawaban ini). MenggunakanTask.Run
untuk memanggil metode dari konteks UI sesuai (sepertiDoVariousThingsFromTheUIThreadAsync
).Task.Run
untuk memanggil metode, tetapi jika adaTask.Run
sekitar semua (atau hampir semua) kode metode, maka itu merupakan anti-pola - biarkan metode itu tetap sinkron danTask.Run
naikkan level.Salah satu hal yang paling penting untuk diingat ketika mendekorasi suatu metode dengan async adalah bahwa setidaknya ada satu operator yang menunggu di dalam metode tersebut. Dalam contoh Anda, saya akan menerjemahkannya seperti yang ditunjukkan di bawah ini menggunakan TaskCompletionSource .
sumber
Ketika Anda menggunakan Task.Run untuk menjalankan metode, Tugas mendapat utas dari threadpool untuk menjalankan metode itu. Jadi dari perspektif utas UI, ini "asinkron" karena tidak memblokir utas UI. Ini bagus untuk aplikasi desktop karena Anda biasanya tidak memerlukan banyak utas untuk menjaga interaksi pengguna.
Namun, untuk aplikasi web setiap permintaan dilayani oleh utas-utas dan dengan demikian jumlah permintaan aktif dapat ditingkatkan dengan menyimpan utas-utas tersebut. Sering menggunakan utas threadpool untuk mensimulasikan operasi async tidak dapat diskalakan untuk aplikasi web.
True Async tidak perlu melibatkan penggunaan utas untuk operasi I / O, seperti akses file / DB dll. Anda dapat membaca ini untuk memahami mengapa operasi I / O tidak memerlukan utas. http://blog.stephencleary.com/2013/11/there-is-no-thread.html
Dalam contoh sederhana Anda, ini adalah perhitungan murni yang terikat CPU, jadi menggunakan Task.Run baik-baik saja.
sumber
I should NOT wrap the synchronous call in Task.Run()
itu benar. Jika ya, Anda hanya perlu mengganti utas. yaitu Anda membuka blokir utas permintaan awal tetapi Anda mengambil utas lain dari threadpool yang bisa digunakan untuk memproses permintaan lain. Satu-satunya hasil adalah overhead saklar konteks ketika panggilan selesai untuk mendapatkan benar-benar nol