Nếu bạn không muốn sử dụng async / await bên trong phương thức của mình, nhưng vẫn "trang trí" nó để có thể sử dụng từ khóa await từ bên ngoài, TaskCompletionSource.cs :
public static Task<T> RunAsync<T>(Func<T> function)
{
if (function == null) throw new ArgumentNullException(“function”);
var tcs = new TaskCompletionSource<T>();
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
T result = function();
tcs.SetResult(result);
}
catch(Exception exc) { tcs.SetException(exc); }
});
return tcs.Task;
}
Từ đây và đây
Để hỗ trợ mô hình như vậy với Nhiệm vụ, chúng ta cần một cách để giữ lại mặt tiền Nhiệm vụ và khả năng tham chiếu một hoạt động không đồng bộ tùy ý như một Nhiệm vụ, nhưng để kiểm soát thời gian của Nhiệm vụ đó theo quy tắc của cơ sở hạ tầng bên dưới cung cấp không đồng bộ và làm như vậy theo cách không tốn kém đáng kể. Đây là mục đích của TaskCompletionSource.
Tôi thấy cũng được sử dụng trong nguồn .NET, vd. WebClient.cs :
[HostProtection(ExternalThreading = true)]
[ComVisible(false)]
public Task<string> UploadStringTaskAsync(Uri address, string method, string data)
{
// Create the task to be returned
var tcs = new TaskCompletionSource<string>(address);
// Setup the callback event handler
UploadStringCompletedEventHandler handler = null;
handler = (sender, e) => HandleCompletion(tcs, e, (args) => args.Result, handler, (webClient, completion) => webClient.UploadStringCompleted -= completion);
this.UploadStringCompleted += handler;
// Start the async operation.
try { this.UploadStringAsync(address, method, data, tcs); }
catch
{
this.UploadStringCompleted -= handler;
throw;
}
// Return the task that represents the async operation
return tcs.Task;
}
Cuối cùng, tôi cũng thấy hữu ích như sau:
Tôi được hỏi câu hỏi này tất cả các thời gian. Hàm ý là phải có một số luồng ở đâu đó đang chặn cuộc gọi I / O tới tài nguyên bên ngoài. Vì vậy, mã không đồng bộ giải phóng luồng yêu cầu, nhưng chỉ bằng chi phí của một luồng khác ở nơi khác trong hệ thống, phải không? Không hoàn toàn không. Để hiểu tại sao thang đo yêu cầu không đồng bộ, tôi sẽ theo dõi một ví dụ (đơn giản hóa) của một cuộc gọi I / O không đồng bộ. Giả sử một yêu cầu cần ghi vào một tệp. Chuỗi yêu cầu gọi phương thức ghi không đồng bộ. WriteAsync được Thư viện lớp cơ sở (BCL) triển khai và sử dụng các cổng hoàn thành cho I / O không đồng bộ của nó. Vì vậy, lệnh gọi WriteAsync được truyền xuống HĐH dưới dạng ghi tệp không đồng bộ. Hệ điều hành sau đó giao tiếp với ngăn xếp trình điều khiển, chuyển dữ liệu để ghi vào gói yêu cầu I / O (IRP). Đây là nơi mà mọi thứ trở nên thú vị: Nếu trình điều khiển thiết bị không thể xử lý IRP ngay lập tức, nó phải xử lý không đồng bộ. Vì vậy, trình điều khiển nói với đĩa bắt đầu ghi và trả về một phản hồi đang chờ xử lý của Wap cho HĐH. Hệ điều hành chuyển đáp ứng của Edward đang chờ xử lý đối với BCL và BCL trả về một tác vụ không hoàn chỉnh cho mã xử lý yêu cầu. Mã xử lý yêu cầu đang chờ tác vụ, trả về một tác vụ chưa hoàn thành từ phương thức đó, v.v. Cuối cùng, mã xử lý yêu cầu kết thúc trả về một tác vụ không hoàn chỉnh cho ASP.NET và luồng yêu cầu được giải phóng để trở về nhóm luồng. Mã xử lý yêu cầu đang chờ tác vụ, trả về một tác vụ chưa hoàn thành từ phương thức đó, v.v. Cuối cùng, mã xử lý yêu cầu kết thúc trả về một tác vụ không hoàn chỉnh cho ASP.NET và luồng yêu cầu được giải phóng để trở về nhóm luồng. Mã xử lý yêu cầu đang chờ tác vụ, trả về một tác vụ chưa hoàn thành từ phương thức đó, v.v. Cuối cùng, mã xử lý yêu cầu kết thúc trả về một tác vụ không hoàn chỉnh cho ASP.NET và luồng yêu cầu được giải phóng để trở về nhóm luồng.
Giới thiệu về Async / Await trên ASP.NET
Nếu mục tiêu là cải thiện khả năng mở rộng (chứ không phải khả năng đáp ứng), thì tất cả phụ thuộc vào sự tồn tại của một I / O bên ngoài cung cấp cơ hội để làm điều đó.