Saya perlu memodifikasi program yang sudah ada dan berisi kode berikut:
var inputs = events.Select(async ev => await ProcessEventAsync(ev))
.Select(t => t.Result)
.Where(i => i != null)
.ToList();
Tetapi ini terasa sangat aneh bagi saya, pertama-tama penggunaan async
dan await
dalam pemilihan. Menurut jawaban Stephen Cleary ini, saya seharusnya bisa menghilangkannya.
Lalu yang kedua Select
yang memilih hasilnya. Bukankah ini berarti tugas sama sekali bukan asinkron dan dilakukan secara serempak (begitu banyak usaha sia-sia), atau akankah tugas dilakukan secara serempak dan ketika selesai, sisa permintaan dieksekusi?
Haruskah saya menulis kode di atas seperti mengikuti menurut jawaban lain oleh Stephen Cleary :
var tasks = await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev)));
var inputs = tasks.Where(result => result != null).ToList();
dan apakah ini sama seperti ini?
var inputs = (await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev))))
.Where(result => result != null).ToList();
Sementara saya sedang mengerjakan proyek ini saya ingin mengubah contoh kode pertama tetapi saya tidak terlalu tertarik untuk mengubah kode async (tampaknya bekerja). Mungkin saya hanya mengkhawatirkan apa-apa dan ketiga sampel kode melakukan hal yang persis sama?
ProcessEventsAsync terlihat seperti ini:
async Task<InputResult> ProcessEventAsync(InputEvent ev) {...}
sumber
Task<InputResult>
denganInputResult
menjadi kelas khusus.Select
hasil dari tugas sebelum AndaWhere
.Result
properti tugasJawaban:
Panggilan ke
Select
valid. Kedua garis ini pada dasarnya identik:(Ada sedikit perbedaan tentang bagaimana pengecualian sinkron akan dilempar dari
ProcessEventAsync
, tetapi dalam konteks kode ini tidak masalah sama sekali.)Ini berarti bahwa kueri sedang memblokir. Jadi itu tidak benar-benar tidak sinkron.
Hancurkan:
pertama-tama akan memulai operasi asinkron untuk setiap peristiwa. Maka baris ini:
akan menunggu operasi tersebut selesai satu per satu (pertama menunggu operasi acara pertama, lalu berikutnya, lalu berikutnya, dll).
Ini adalah bagian yang saya tidak peduli, karena itu memblokir dan juga akan membungkus pengecualian
AggregateException
.Ya, kedua contoh itu setara. Keduanya memulai semua operasi asinkron (
events.Select(...)
), lalu menunggu secara tak sinkron untuk semua operasi selesai dalam urutan apa pun (await Task.WhenAll(...)
), kemudian melanjutkan dengan sisa pekerjaan (Where...
).Kedua contoh ini berbeda dari kode aslinya. Kode asli diblokir dan akan membungkus pengecualian dalam
AggregateException
.sumber
AggregateException
saya akan mendapatkan beberapa pengecualian terpisah dalam kode kedua?Result
itu akan dibungkusAggregateException
.stuff.Select(x => x.Result);
denganawait Task.WhenAll(stuff)
Kode yang ada berfungsi, tetapi memblokir utas.
membuat Tugas baru untuk setiap acara, tetapi
memblokir utas menunggu setiap tugas baru berakhir.
Di sisi lain kode Anda menghasilkan hasil yang sama tetapi tetap tidak sinkron.
Hanya satu komentar pada kode pertama Anda. Garis ini
akan menghasilkan Tugas tunggal sehingga variabel harus dinamai dalam singular.
Akhirnya kode terakhir Anda menghasilkan hal yang sama tetapi lebih ringkas
Untuk referensi: Task.Wait / Task.WhenAll
sumber
tasks
variabel, Anda sepenuhnya benar. Pilihan yang mengerikan, mereka bahkan bukan tugas karena mereka segera ditunggu. Saya hanya akan meninggalkan pertanyaan seperti ituDengan metode saat ini tersedia di Linq terlihat sangat jelek:
Semoga versi berikut. NET akan datang dengan tooling yang lebih elegan untuk menangani koleksi tugas dan tugas koleksi.
sumber
Saya menggunakan kode ini:
seperti ini:
sumber
Func<TSource, Task<TResult>> method
berisi yangother params
disebutkan pada bit kode kedua?Select()
, jadi drop-in yang elegan.Saya lebih suka ini sebagai metode ekstensi:
Sehingga dapat digunakan dengan metode chaining:
sumber
Wait
itu ketika sebenarnya tidak menunggu. Itu menciptakan tugas yang selesai ketika semua tugas selesai. Sebut sajaWhenAll
, sepertiTask
metode yang ditiru. Tidak ada gunanya juga metode iniasync
. Panggil sajaWhenAll
dan selesaikan saja.WhenAll
mengembalikan daftar yang dievaluasi (tidak dievaluasi dengan malas), argumen dapat dibuat untuk menggunakanTask<T[]>
jenis pengembalian untuk menandakan itu. Ketika ditunggu, ini masih bisa menggunakan Linq, tetapi juga mengomunikasikan bahwa itu tidak malas.