Tôi có một phương pháp đồng bộ đơn giản, nhìn như thế này:cách khác nhau của không đồng bộ trở về một bộ sưu tập (với C# 7 tính năng)
public IEnumerable<Foo> MyMethod(Source src)
{
// returns a List of Oof objects from a web service
var oofs = src.LoadOofsAsync().Result;
foreach(var oof in oofs)
{
// transforms an Oof object to a Foo object
yield return Transform(oof);
}
}
Kể từ khi phương pháp này là một phần của một ứng dụng web, nó là tốt để sử dụng tất cả tài nguyên hiệu quả nhất có thể. Do đó, tôi muốn thay đổi phương thức thành một số không đồng bộ. Các tùy chọn đơn giản nhất là để làm một cái gì đó như thế này:
public async Task<IEnumerable<Foo>> MyMethodAsync(Source src)
{
var oofs = await src.LoadOofsAsync();
return oofs.Select(oof => Transform(oof));
}
Tôi không phải là một chuyên gia về một trong hai async
/await
hoặc IEnumerable
. Tuy nhiên, từ những gì tôi hiểu, sử dụng cách tiếp cận này "giết chết" những lợi ích của IEnumerable
, bởi vì nhiệm vụ được chờ đợi cho đến khi toàn bộ bộ sưu tập được tải, do đó bỏ qua "sự lười biếng" của bộ sưu tập IEnumerable
.
Trên các bài đăng StackOverflow khác, tôi đã đọc một số đề xuất để sử dụng Rx.NET (hoặc System.Reactive). Nhanh chóng duyệt qua tài liệu tôi đã đọc rằng IObservable<T>
là sự thay thế không đồng bộ của chúng với IEnumerable<T>
. Tuy nhiên, bằng cách sử dụng cách tiếp cận ngây thơ và cố gắng gõ sau đây chỉ không làm việc:
public async IObservable<Foo> MyMethodReactive(Source src)
{
var oofs = await src.LoadOofsAsync();
foreach(var oof in oofs)
{
yield return Transform(oof);
}
}
tôi đã nhận một lỗi biên dịch, mà IObservable<T>
không thực hiện không GetEnumerator()
, cũng không GetAwaiter()
- do đó nó không thể sử dụng cả hai yield
và async
. Tôi đã không đọc các tài liệu hướng dẫn của Rx.NET sâu hơn, vì vậy tôi có lẽ chỉ cần sử dụng thư viện không chính xác. Nhưng tôi không muốn dành thời gian học một khuôn khổ mới để sửa đổi một phương pháp duy nhất.
Với possibilities in C# 7 mới, giờ đây bạn có thể triển khai các loại tùy chỉnh. Vì vậy, về mặt lý thuyết, tôi có thể thực hiện một số IAsyncEnumerable
, định nghĩa cả hai phương thức GetEnumerator()
và GetAwaiter()
. Tuy nhiên, từ kinh nghiệm trước đây của tôi, tôi nhớ một nỗ lực không thành công để tạo ra một thực hiện tùy chỉnh GetEnumerator()
... Tôi đã kết thúc với một danh sách đơn giản, ẩn trong một container.
Như vậy chúng ta có 4 phương pháp có thể để giải quyết các nhiệm vụ:
- Giữ đồng bộ mã, nhưng với
IEnumerable
- Thay đổi nó để không đồng bộ, nhưng quấn
IEnumerable
trong mộtTask<T>
- Tìm hiểu và sử dụng Rx .NET (System.Reactive)
- Tạo một tùy chỉnh
IAsyncEnumerable
với các tính năng C# 7
Những lợi ích và hạn chế của mỗi lần thử này là gì? Điều nào trong số đó có tác động đáng kể nhất đối với việc sử dụng tài nguyên?
Ngoài ra còn có một cách thứ 5 rất dễ dàng: https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/introduction-to-plinq: 'AsParallel()' –
@ AdrianoRepetti AsParallel có phù hợp với nhiệm vụ I/O-bound không? – MickyD
Nếu bạn quan tâm đến lợi ích "giết chóc", điều bạn thực sự nên lo lắng là 'src.LoadOofsAsync()' - rằng * đã * trả về một 'Tác vụ>'. Bao bọc thêm một số sự không đồng bộ hơn sẽ không giúp ích gì, trừ khi 'Biến đổi' thực sự tốn kém và có thể được thực hiện song song/chờ đợi một cách có ý nghĩa. –