Można byłaby ten fragment tak:
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;
}
Podobno jedyną korzyścią byłoby to daje to bardziej efektywne wykorzystanie puli wątków, ponieważ nie zawsze cały wątek, aby opóźnienie zdarzyć.
W zależności od sposobu uzyskania outcome
, może być o wiele bardziej wydajnych sposobów wykonania tego zadania za pomocą async/await
. Często zdarza się, że masz coś takiego, jak GetOutcomeAsync()
, które spowodowałoby asynchroniczne wywołanie usługi sieci Web, bazy danych lub gniazda, w naturalny sposób, więc wystarczy wykonać var outcome = await GetOutcomeAsync()
.
Należy wziąć pod uwagę, że WaitForItToWork
zostanie podzielony na części przez kompilator, a część z linii await
będzie kontynuowana asynchronicznie. Here's być może najlepszym wytłumaczeniem, jak to zrobić wewnętrznie. Rzecz w tym, że zwykle w pewnym momencie kodu trzeba zsynchronizować wynik zadania asynchronicznego. Np .:
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());
}
Mogłeś po prostu zrobić to:
private async void Form1_Load(object sender, EventArgs e)
{
bool result = await WaitForItToWork();
MessageBox.Show("WaitForItToWork done:" + result.toString()); // true or false
}
To byłoby jednak zrobić Form1_Load
metodę asynchroniczny też.
[UPDATE]
Poniżej moja próba zilustrować co async/await
faktycznie robi w tej sprawie. Stworzyłem dwie wersje tej samej logiki, WaitForItToWorkAsync
(używając async/await
) i WaitForItToWorkAsyncTap
(używając TAP pattern bez async/await
). Wersja pierwsza jest dość trywialna, w przeciwieństwie do drugiej. Tak więc, podczas gdy async/await
jest w dużej mierze syntaktycznym cukrem kompilatora, sprawia, że kod asynchroniczny jest znacznie łatwiejszy do napisania i zrozumienia.
// 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());
}
Kilka słów o gwintowania. Nie ma tu żadnych dodatkowych wątków jawnie utworzonych tutaj.Wewnętrznie implementacja Task.Delay()
może wykorzystywać wątki puli (podejrzewam, że używają one Timer Queues), ale w tym konkretnym przykładzie (aplikacja WinForms) kontynuacja po await
stanie się w tym samym wątku UI. W innych środowiskach wykonawczych (na przykład w aplikacji konsolowej) może być kontynuowany w innym wątku. IMO, this article autorstwa Stephena Cleary'a jest koniecznym do zrozumienia pojęciem gwintowania async/await
.
Czy można to nazwać po prostu wykonaniem 'this.WaitForItToWork();' - czy biblioteka asynchroniczna zajmie się wątkiem dla mnie? – Chris
Nazwałbyś to jak 'poczekaj na tę funkcję.WaitForItToWork()', a cały łańcuch wywołań musi być refaktoryzowany, aby to wspierać ... Rozwiążę moją odpowiedź, aby dodać więcej informacji. – Noseratio
@ Chris: musisz pamiętać, aby użyć słowa kluczowego "czekaj". Zasada kciuka: Zawsze "oczekuj" musi być połączona z funkcją "asynchroniczną". Tak więc powinieneś zrobić coś takiego, jak czekać na WaitForItToWork(); –