Зачекайте недійсного методу асинхронізації


155

Як я можу чекати void async метод закінчить свою роботу?

Наприклад, у мене є функція, як показано нижче:

async void LoadBlahBlah()
{
    await blah();
    ...
}

тепер я хочу переконатися, що все завантажено, перш ніж продовжувати десь ще.

Відповіді:


234

Найкраща практика - позначати функцію async voidлише в тому випадку, якщо це метод вогню і забуття, якщо ви хочете чекати, слід позначити її як async Task.

Якщо ви все ще хочете чекати, то оберніть це так await Task.Run(() => blah())


бла, очевидно, повертає завдання і підписує асинхрон, чому б ви його називали без очікування. навіть VS попередить вас про це.
batmaci

8
await Task.Run(() => An_async_void_method_I_can_not_modify_now())
тематичне поле

1
await Task.Run(() => blah())вводить в оману. Це не чекає завершення функції асинхронізації blah, вона просто чекає (тривіального) створення завдання та продовжується безпосередньо перед blah()її виконанням.
Джонатан Лідбек

@JonathanLidbeck висловлювання "продовжується негайно, перш ніж бла не помилиться. Спробуйте" чекайте Task.Run (() => Thread.Sleep (10_000)) ", завдання очікується протягом 10 секунд + перед виконанням будь-якого наступного рядка
Rohit Sharma

@RohitSharma, це правильно для вашого прикладу, але Thread.Sleepце не асинхроніка. Це питання стосується очікування async voidфункції, скажімоasync void blah() { Task.Delay(10000); }
Джонатан Лідбек

59

Якщо ви можете змінити підпис своєї функції, async Taskтоді ви можете використовувати код, представлений тут


27

Найкраще рішення - використовувати async Task. Вам слід уникати async voidз кількох причин, однією з яких є комбінованість.

Якщо метод не може бути примушений повернутися Task(наприклад, це обробник подій), тоді ви можете використовувати SemaphoreSlimсигнал методу, коли він збирається вийти. Розглянемо це в finallyблоці.


1

зробіть AutoResetEvent, зателефонуйте до функції, а потім зачекайте на AutoResetEvent, а потім встановіть її всередині асинхронної порожнечі, коли ви знаєте, що це зроблено.

Ви також можете зачекати на Завдання, яке повертається з вашої асинхронізації порожнечі


1

Вам не потрібно нічого робити вручну, 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()
MBZ

10
На жаль, асинхронізовані недійсні методи не призупиняють виконання (на відміну від методів задач async)
ghord

-1

Я знаю, що це давнє питання, але це все ще проблема, над якою я продовжую ходити, але все ще не існує чіткого рішення, як це зробити правильно, коли використовується метод 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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.