Diberikan tiga tugas - FeedCat()
, SellHouse()
danBuyCar()
, ada dua kasus menarik: apakah semuanya diselesaikan secara serempak (karena alasan tertentu, mungkin caching atau kesalahan), atau tidak.
Katakanlah kita punya, dari pertanyaan:
Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = SellHouse();
Task<Tesla> z = BuyCar();
// what here?
}
Sekarang, pendekatan sederhana adalah:
Task.WhenAll(x, y, z);
tapi ... itu tidak nyaman untuk memproses hasil; kami biasanya ingin await
itu:
async Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = SellHouse();
Task<Tesla> z = BuyCar();
await Task.WhenAll(x, y, z);
// presumably we want to do something with the results...
return DoWhatever(x.Result, y.Result, z.Result);
}
tetapi ini tidak banyak overhead dan mengalokasikan berbagai array (termasuk params Task[]
array) dan daftar (secara internal). Ini bekerja, tetapi itu bukan IMO yang bagus. Dalam banyak hal lebih mudah untuk menggunakan async
operasi dan hanya await
masing - masing pada gilirannya:
async Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = SellHouse();
Task<Tesla> z = BuyCar();
// do something with the results...
return DoWhatever(await x, await y, await z);
}
Berlawanan dengan beberapa komentar di atas, menggunakan await
bukannya tidakTask.WhenAll
membuat perbedaan pada bagaimana tugas dijalankan (secara bersamaan, secara berurutan, dll). Pada level tertinggi, Task.WhenAll
mendahului dukungan compiler yang baik untuk async
/ await
, dan berguna ketika hal-hal itu tidak ada . Ini juga berguna ketika Anda memiliki array tugas yang sewenang-wenang, bukan 3 tugas yang tersembunyi.
Tapi: kita masih memiliki masalah yang async
/ await
menghasilkan banyak compiler kebisingan untuk kelanjutan. Jika kemungkinan tugas tersebut benar-benar selesai secara sinkron, maka kami dapat mengoptimalkannya dengan membangun jalur sinkron dengan fallback asinkron:
Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = SellHouse();
Task<Tesla> z = BuyCar();
if(x.Status == TaskStatus.RanToCompletion &&
y.Status == TaskStatus.RanToCompletion &&
z.Status == TaskStatus.RanToCompletion)
return Task.FromResult(
DoWhatever(a.Result, b.Result, c.Result));
// we can safely access .Result, as they are known
// to be ran-to-completion
return Awaited(x, y, z);
}
async Task Awaited(Task<Cat> a, Task<House> b, Task<Tesla> c) {
return DoWhatever(await x, await y, await z);
}
Pendekatan "jalur sinkronisasi dengan async fallback" ini semakin umum terutama dalam kode kinerja tinggi di mana penyelesaian sinkron relatif sering. Perhatikan itu tidak akan membantu sama sekali jika penyelesaiannya selalu benar-benar tidak sinkron.
Hal-hal tambahan yang berlaku di sini:
dengan C # baru-baru ini, pola umum untuk async
metode fallback umumnya diterapkan sebagai fungsi lokal:
Task<string> DoTheThings() {
async Task<string> Awaited(Task<Cat> a, Task<House> b, Task<Tesla> c) {
return DoWhatever(await a, await b, await c);
}
Task<Cat> x = FeedCat();
Task<House> y = SellHouse();
Task<Tesla> z = BuyCar();
if(x.Status == TaskStatus.RanToCompletion &&
y.Status == TaskStatus.RanToCompletion &&
z.Status == TaskStatus.RanToCompletion)
return Task.FromResult(
DoWhatever(a.Result, b.Result, c.Result));
// we can safely access .Result, as they are known
// to be ran-to-completion
return Awaited(x, y, z);
}
lebih memilih ValueTask<T>
untuk Task<T>
jika ada kesempatan baik hal-hal yang pernah benar-benar serentak dengan banyak kembali nilai-nilai yang berbeda:
ValueTask<string> DoTheThings() {
async ValueTask<string> Awaited(ValueTask<Cat> a, Task<House> b, Task<Tesla> c) {
return DoWhatever(await a, await b, await c);
}
ValueTask<Cat> x = FeedCat();
ValueTask<House> y = SellHouse();
ValueTask<Tesla> z = BuyCar();
if(x.IsCompletedSuccessfully &&
y.IsCompletedSuccessfully &&
z.IsCompletedSuccessfully)
return new ValueTask<string>(
DoWhatever(a.Result, b.Result, c.Result));
// we can safely access .Result, as they are known
// to be ran-to-completion
return Awaited(x, y, z);
}
jika mungkin, lebih memilih IsCompletedSuccessfully
untuk Status == TaskStatus.RanToCompletion
; ini sekarang ada di .NET Core untuk Task
, dan di mana-mana untukValueTask<T>