Lập trình không đồng bộ "phát triển" thông qua cơ sở mã. Nó đã được so sánh với một virus zombie . Giải pháp tốt nhất là cho phép nó phát triển, nhưng đôi khi điều đó là không thể.
Tôi đã viết một vài loại trong thư viện Nito.AsyncEx của mình để xử lý cơ sở mã không đồng bộ một phần. Không có giải pháp nào hoạt động trong mọi tình huống.
Giải pháp A
Nếu bạn có một phương pháp không đồng bộ đơn giản mà không cần đồng bộ hóa trở lại bối cảnh của nó, thì bạn có thể sử dụng Task.WaitAndUnwrapException:
var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();
Bạn không muốn sử dụng Task.Waithoặc Task.Resultvì chúng bao gồm các ngoại lệ AggregateException.
Giải pháp này chỉ phù hợp nếu MyAsyncMethodkhông đồng bộ hóa trở lại bối cảnh của nó. Nói cách khác, mỗi awaittrong MyAsyncMethodnên kết thúc bằng ConfigureAwait(false). Điều này có nghĩa là nó không thể cập nhật bất kỳ thành phần UI nào hoặc truy cập vào bối cảnh yêu cầu ASP.NET.
Giải pháp B
Nếu MyAsyncMethodkhông cần phải đồng bộ hóa trở lại bối cảnh của nó, thì bạn có thể sử dụng AsyncContext.RunTaskđể cung cấp một bối cảnh lồng nhau:
var result = AsyncContext.RunTask(MyAsyncMethod).Result;
* Cập nhật ngày 14 tháng 4 năm 2014: Trong các phiên bản gần đây hơn của thư viện, API như sau:
var result = AsyncContext.Run(MyAsyncMethod);
(Bạn có thể sử dụng Task.Resulttrong ví dụ này vì RunTasksẽ truyền Taskngoại lệ).
Lý do bạn có thể cần AsyncContext.RunTaskthay vì Task.WaitAndUnwrapExceptionlà vì khả năng bế tắc khá tinh tế xảy ra trên WinForms / WPF / SL / ASP.NET:
- Phương thức đồng bộ gọi phương thức không đồng bộ, thu được a
Task.
- Phương thức đồng bộ thực hiện chờ chặn trên
Task.
- Các
asyncphương pháp sử dụng awaitmà không cần ConfigureAwait.
- Không
Taskthể hoàn thành trong tình huống này vì nó chỉ hoàn thành khi asyncphương thức kết thúc; các asyncphương pháp có thể không đầy đủ vì nó đang cố gắng sắp xếp tiếp nối của nó đến SynchronizationContext, và WinForms / WPF / SL / ASP.NET sẽ không cho phép việc tiếp tục chạy vì phương pháp đồng bộ vẫn đang chạy trong bối cảnh đó.
Đây là một lý do tại sao nên sử dụng ConfigureAwait(false)trong mọi asyncphương pháp càng nhiều càng tốt.
Giải pháp C
AsyncContext.RunTasksẽ không hoạt động trong mọi kịch bản. Ví dụ: nếu asyncphương thức chờ một cái gì đó yêu cầu sự kiện UI hoàn thành, thì bạn sẽ bế tắc ngay cả với bối cảnh lồng nhau. Trong trường hợp đó, bạn có thể bắt đầu asyncphương thức trên nhóm luồng:
var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();
Tuy nhiên, giải pháp này đòi hỏi một MyAsyncMethodthứ sẽ hoạt động trong bối cảnh nhóm luồng. Vì vậy, nó không thể cập nhật các thành phần UI hoặc truy cập vào bối cảnh yêu cầu ASP.NET. Và trong trường hợp đó, bạn cũng có thể thêm ConfigureAwait(false)vào các awaitcâu lệnh của nó và sử dụng giải pháp A.
Cập nhật, 2019-05-01: "Các thực tiễn tồi tệ nhất" hiện tại có trong một bài viết MSDN tại đây .