Với ba nhiệm vụ - FeedCat()
, SellHouse()
và BuyCar()
, có hai trường hợp thú vị: hoặc là họ tất cả đồng bộ đầy đủ (đối với một số lý do, có lẽ bộ nhớ đệm hoặc một lỗi), hoặc họ không.
Hãy nói rằng chúng ta có, từ câu hỏi:
Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = SellHouse();
Task<Tesla> z = BuyCar();
// what here?
}
Bây giờ, một cách tiếp cận đơn giản sẽ là:
Task.WhenAll(x, y, z);
nhưng ... điều đó không thuận tiện để xử lý kết quả; chúng tôi thường muốn await
điều đó:
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);
}
nhưng điều này không có nhiều chi phí và phân bổ các mảng khác nhau (bao gồm cả params Task[]
mảng) và danh sách (nội bộ). Nó hoạt động, nhưng nó không phải là IMO tuyệt vời. Theo nhiều cách, việc sử dụng một thao tác sẽ đơn giản hơnasync
và chỉ await
lần lượt từng thao tác :
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);
}
Trái với một số ý kiến trên, sử dụng await
thay vì Task.WhenAll
làm cho có sự khác biệt như thế nào các nhiệm vụ chạy (đồng thời, liên tục, vv). Ở mức cao nhất, Task.WhenAll
có trước hỗ trợ trình biên dịch tốt cho async
/ await
và rất hữu ích khi những thứ đó không tồn tại . Nó cũng hữu ích khi bạn có một loạt các nhiệm vụ tùy ý, thay vì 3 nhiệm vụ kín đáo.
Nhưng: chúng ta vẫn có vấn đề là async
/ await
tạo ra nhiều tiếng ồn của trình biên dịch để tiếp tục. Nếu có khả năng các tác vụ có thể thực sự hoàn thành đồng bộ, thì chúng ta có thể tối ưu hóa điều này bằng cách xây dựng theo một đường dẫn đồng bộ với dự phòng không đồng bộ:
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);
}
Phương pháp "đường dẫn đồng bộ hóa với dự phòng không đồng bộ" này ngày càng phổ biến, đặc biệt là trong mã hiệu suất cao, nơi việc hoàn thành đồng bộ là tương đối thường xuyên. Lưu ý rằng nó sẽ không giúp ích gì cả nếu việc hoàn thành luôn không đồng bộ.
Những điều bổ sung áp dụng ở đây:
với C # gần đây, một mẫu chung cho async
phương thức dự phòng thường được triển khai như một hàm cục bộ:
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);
}
thích ValueTask<T>
để Task<T>
nếu có một cơ hội tốt cho những thứ không bao giờ hoàn toàn đồng bộ với nhiều giá trị lợi nhuận khác nhau:
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);
}
nếu có thể, thích IsCompletedSuccessfully
đến Status == TaskStatus.RanToCompletion
; điều này hiện tồn tại trong .NET Core cho Task
và ở mọi nơi choValueTask<T>