Відповіді:
Найкраща практика - позначати функцію async void
лише в тому випадку, якщо це метод вогню і забуття, якщо ви хочете чекати, слід позначити її як async Task
.
Якщо ви все ще хочете чекати, то оберніть це так await Task.Run(() => blah())
await Task.Run(() => An_async_void_method_I_can_not_modify_now())
await Task.Run(() => blah())
вводить в оману. Це не чекає завершення функції асинхронізації blah
, вона просто чекає (тривіального) створення завдання та продовжується безпосередньо перед blah()
її виконанням.
Thread.Sleep
це не асинхроніка. Це питання стосується очікування async void
функції, скажімоasync void blah() { Task.Delay(10000); }
Найкраще рішення - використовувати async Task
. Вам слід уникати async void
з кількох причин, однією з яких є комбінованість.
Якщо метод не може бути примушений повернутися Task
(наприклад, це обробник подій), тоді ви можете використовувати SemaphoreSlim
сигнал методу, коли він збирається вийти. Розглянемо це в finally
блоці.
зробіть AutoResetEvent, зателефонуйте до функції, а потім зачекайте на AutoResetEvent, а потім встановіть її всередині асинхронної порожнечі, коли ви знаєте, що це зроблено.
Ви також можете зачекати на Завдання, яке повертається з вашої асинхронізації порожнечі
Вам не потрібно нічого робити вручну, await
ключове слово призупиняє виконання функції до blah()
повернення.
private async void SomeFunction()
{
var x = await LoadBlahBlah(); <- Function is not paused
//rest of the code get's executed even if LoadBlahBlah() is still executing
}
private async Task<T> LoadBlahBlah()
{
await DoStuff(); <- function is paused
await DoMoreStuff();
}
T
це тип blah()
повернення об'єкта
Ви дійсно не await
можете void
функціонувати, тому LoadBlahBlah()
не може бутиvoid
LoadBlahBlah()
завершення, ніblah()
Я знаю, що це давнє питання, але це все ще проблема, над якою я продовжую ходити, але все ще не існує чіткого рішення, як це зробити правильно, коли використовується метод async / очікувати в методі підпису анулювання анулювання.
Однак я помітив, що .Wait () працює належним чином всередині методу void.
а оскільки async void and void мають однаковий підпис, вам може знадобитися зробити наступне.
void LoadBlahBlah()
{
blah().Wait(); //this blocks
}
Досить заплутано асинхронізація / очікування не блокується при наступному коді.
async void LoadBlahBlah()
{
await blah(); //this does not block
}
Коли ви декомпілюєте свій код, я гадаю, що асинхронізація void створює внутрішнє завдання (подібно до завдання async), але оскільки підпис не підтримує повернення внутрішніх завдань
це означає, що внутрішньо асинхронний метод анулювання все ще зможе "очікувати" внутрішньо асинхронних методів. але зовні не в змозі знати, коли внутрішня задача виконана.
Отже, мій висновок полягає в тому, що асинхроністика недійсна працює за призначенням, і якщо вам потрібен зворотний зв'язок із внутрішнього завдання, тоді вам потрібно використовувати підпис Async Task.
сподіваюся, моє бурчання має сенс для тих, хто також шукає відповіді.
Редагувати: Я зробив приклад коду і розпакував його, щоб побачити, що насправді відбувається.
static async void Test()
{
await Task.Delay(5000);
}
static async Task TestAsync()
{
await Task.Delay(5000);
}
Перетворюється (редагувати: я знаю, що код тіла не тут, а в державних машинах, але штатні машини в основному були однаковими, тому я не намагався їх додавати)
private static void Test()
{
<Test>d__1 stateMachine = new <Test>d__1();
stateMachine.<>t__builder = AsyncVoidMethodBuilder.Create();
stateMachine.<>1__state = -1;
AsyncVoidMethodBuilder <>t__builder = stateMachine.<>t__builder;
<>t__builder.Start(ref stateMachine);
}
private static Task TestAsync()
{
<TestAsync>d__2 stateMachine = new <TestAsync>d__2();
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.<>1__state = -1;
AsyncTaskMethodBuilder <>t__builder = stateMachine.<>t__builder;
<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
ні AsyncVoidMethodBuilder, ні AsyncTaskMethodBuilder насправді не мають жодного коду методу Start, який би натякав на їх блокування, і завжди запускався б асинхронно після їх запуску.
тобто без Завдання, що повертається, не було б можливості перевірити, чи воно завершено.
як очікувалося, він запускає лише асинхронізацію Завдання, а потім продовжує в коді. і завдання асинхронізації, спочатку вона запускає завдання, а потім повертає її.
тому я гадаю, що моєю відповіддю було б ніколи не використовувати асинхронну недійсність, якщо вам потрібно знати, коли завдання виконано, саме для цього виконується завдання Async.