Вы можете реорганизовать этот фрагмент так:
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;
}
По-видимому, единственное преимущество это даст вам более эффективное использование пула потоков, потому что не всегда имеет целую нить, чтобы сделать задержку бывает.
В зависимости от того, как вы получаете outcome
, могут быть гораздо более эффективные способы выполнения этой работы с использованием async/await
. Часто у вас может быть что-то вроде GetOutcomeAsync()
, что сделает асинхронно веб-сервис, базу данных или сокет естественным образом, так что вы просто сделаете var outcome = await GetOutcomeAsync()
.
Важно учитывать, что WaitForItToWork
будет разделен на части компилятором, а часть от await
линия будет продолжена асинхронно. Here's возможно лучший объяснение о том, как это делается внутренне. Дело в том, что обычно в какой-то момент вашего кода вам нужно синхронизировать результат асинхронной задачи. Например .:
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());
}
Вы могли бы просто сделать это:
private async void Form1_Load(object sender, EventArgs e)
{
bool result = await WaitForItToWork();
MessageBox.Show("WaitForItToWork done:" + result.toString()); // true or false
}
Это будет, однако, сделать Form1_Load
метод асинхронной тоже.
[UPDATE]
Ниже моя попытка проиллюстрировать то, что async/await
на самом деле делает в этом случае. Я создал две версии той же логики, WaitForItToWorkAsync
(с использованием async/await
) и WaitForItToWorkAsyncTap
(с использованием TAP pattern без async/await
). Версия frist довольно тривиальна, в отличие от второй. Таким образом, хотя async/await
в значительной степени синтаксический сахар компилятора, он делает асинхронный код намного проще писать и понимать.
// 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());
}
Несколько слов о многопоточности. Никаких дополнительных потоков, явно созданных здесь.Внутренне, реализация Task.Delay()
может использовать потоки пула (я подозреваю, что они используют Timer Queues), но в этом конкретном примере (приложение WinForms) продолжение после await
произойдет в одном и том же потоке пользовательского интерфейса. В других средах исполнения (например, консольном приложении) он может продолжаться в другом потоке. IMO, this article от Stephen Cleary является обязательным для понимания концепциями потоковой передачи async/await
.
Я бы назвал это просто, выполнив 'this.WaitForItToWork();' - будет ли библиотека async заботиться о потоке для меня? – Chris
Вы бы назвали это как 'await this.WaitForItToWork()', и вся цепочка звонков должна быть реорганизована для поддержки этого ... Я расскажу о своем ответе, чтобы включить в него больше информации. – Noseratio
@ Крис: вы должны помнить, что используйте ключевое слово «ожидание». Правило большого пальца: всегда «ожидание» должно сочетаться с функцией «async». Итак, вы должны сделать что-то вроде WaitForItToWork(); –