Bạn có thể cấu trúc lại đoạn đó như thế này:
async Task<bool> WaitForItToWork()
{
bool succeeded = false;
while (!succeeded)
{
// do work
succeeded = outcome; // if it worked, make as succeeded, else retry
await Task.Delay(1000); // arbitrary delay
}
return succeeded;
}
Rõ ràng, lợi ích duy nhất nó sẽ cung cấp cho bạn là sử dụng hiệu quả hơn các hồ bơi thread, bởi vì nó không phải lúc nào mất cả một sợi để làm cho sự chậm trễ xảy ra.
Tùy thuộc vào cách bạn có được outcome
, có thể có nhiều cách hiệu quả hơn để hoàn thành công việc này bằng cách sử dụng async/await
. Thường thì bạn có thể có một cái gì đó như GetOutcomeAsync()
mà sẽ làm cho một dịch vụ web, cơ sở dữ liệu hoặc cuộc gọi socket không đồng bộ một cách tự nhiên, vì vậy bạn chỉ cần làm var outcome = await GetOutcomeAsync()
.
Điều quan trọng là phải xem xét rằng WaitForItToWork
sẽ được chia thành các phần theo trình biên dịch và một phần từ dòng await
sẽ được tiếp tục không đồng bộ. Here's có lẽ là giải thích tốt nhất về cách thực hiện nội bộ. Vấn đề là, thường là tại một số điểm của mã của bạn, bạn cần phải đồng bộ hóa trên kết quả của nhiệm vụ không đồng bộ. Ví dụ .:
private void Form1_Load(object sender, EventArgs e)
{
Task<bool> task = WaitForItToWork();
task.ContinueWith(_ => {
MessageBox.Show("WaitForItToWork done:" + task.Result.toString()); // true or false
}, TaskScheduler.FromCurrentSynchronizationContext());
}
Bạn có thể chỉ đơn giản là thực hiện điều này:
private async void Form1_Load(object sender, EventArgs e)
{
bool result = await WaitForItToWork();
MessageBox.Show("WaitForItToWork done:" + result.toString()); // true or false
}
Đó tuy nhiên sẽ làm cho Form1_Load
một phương pháp async quá.
[UPDATE]
Dưới đây là nỗ lực của tôi để minh họa những gì async/await
thực sự thực hiện trong trường hợp này. Tôi đã tạo hai phiên bản có cùng một logic, WaitForItToWorkAsync
(sử dụng async/await
) và WaitForItToWorkAsyncTap
(sử dụng TAP pattern mà không cần async/await
). Phiên bản frist khá tầm thường, không giống phiên bản thứ hai. Vì vậy, trong khi async/await
phần lớn là đường cú pháp của trình biên dịch, nó làm cho mã không đồng bộ dễ dàng hơn nhiều để viết và hiểu.
// fake outcome() method for testing
bool outcome() { return new Random().Next(0, 99) > 50; }
// with async/await
async Task<bool> WaitForItToWorkAsync()
{
var succeeded = false;
while (!succeeded)
{
succeeded = outcome(); // if it worked, make as succeeded, else retry
await Task.Delay(1000);
}
return succeeded;
}
// without async/await
Task<bool> WaitForItToWorkAsyncTap()
{
var context = TaskScheduler.FromCurrentSynchronizationContext();
var tcs = new TaskCompletionSource<bool>();
var succeeded = false;
Action closure = null;
closure = delegate
{
succeeded = outcome(); // if it worked, make as succeeded, else retry
Task.Delay(1000).ContinueWith(delegate
{
if (succeeded)
tcs.SetResult(succeeded);
else
closure();
}, context);
};
// start the task logic synchronously
// it could end synchronously too! (e.g, if we used 'Task.Delay(0)')
closure();
return tcs.Task;
}
// start both tasks and handle the completion of each asynchronously
private void StartWaitForItToWork()
{
WaitForItToWorkAsync().ContinueWith((t) =>
{
MessageBox.Show("WaitForItToWorkAsync complete: " + t.Result.ToString());
}, TaskScheduler.FromCurrentSynchronizationContext());
WaitForItToWorkAsyncTap().ContinueWith((t) =>
{
MessageBox.Show("WaitForItToWorkAsyncTap complete: " + t.Result.ToString());
}, TaskScheduler.FromCurrentSynchronizationContext());
}
// await for each tasks (StartWaitForItToWorkAsync itself is async)
private async Task StartWaitForItToWorkAsync()
{
bool result = await WaitForItToWorkAsync();
MessageBox.Show("WaitForItToWorkAsync complete: " + result.ToString());
result = await WaitForItToWorkAsyncTap();
MessageBox.Show("WaitForItToWorkAsyncTap complete: " + result.ToString());
}
Một vài từ trên luồng. Không có chủ đề bổ sung được tạo rõ ràng ở đây.Bên trong, thực hiện Task.Delay()
có thể sử dụng chủ đề pool (tôi nghi ngờ họ sử dụng Timer Queues), nhưng trong ví dụ cụ thể này (một ứng dụng WinForms), việc tiếp tục sau await
sẽ xảy ra trên cùng một chuỗi giao diện người dùng. Trong các môi trường thực thi khác (ví dụ: ứng dụng bảng điều khiển), nó có thể tiếp tục trên một chuỗi khác. IMO, this article bởi Stephen Cleary là phải đọc để hiểu khái niệm luồng async/await
.
Tôi có thể gọi điều này đơn giản bằng cách thực hiện 'this.WaitForItToWork();' - thư viện async sẽ xử lý luồng cho tôi? – Chris
Bạn sẽ gọi nó như 'await this.WaitForItToWork()', và toàn bộ chuỗi các cuộc gọi phải được cấu trúc lại để hỗ trợ điều này ... Tôi sẽ giải thích về câu trả lời của tôi để bao gồm thêm thông tin này. – Noseratio
@Chris: bạn phải nhớ sử dụng từ khóa "đang chờ". Quy tắc của ngón tay cái: Luôn luôn "chờ đợi" phải được kết hợp với chức năng "không đồng bộ". Vì vậy, bạn nên làm một cái gì đó như chờ đợi WaitForItToWork(); –