Ответ 1
Вы можете реорганизовать этот фрагмент следующим образом:
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
будет продолжена асинхронно. Вот, возможно, лучшее объяснение того, как это делается внутри страны. Дело в том, что обычно в какой-то момент вашего кода вам нужно синхронизировать результат асинхронной задачи. Например:.
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
асинхронным методом.
[ОБНОВЛЕНИЕ]
Ниже я попытался проиллюстрировать, что в действительности делает 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, эта статья Стивена Клири является обязательным для понимания концепциями потоковой передачи async/await
.