Ждите void асинхронный метод


155

Как я могу ждать, чтобы void asyncметод закончил свою работу?

например, у меня есть функция, как показано ниже:

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

Теперь я хочу убедиться, что все загружено, прежде чем продолжить в другом месте.

Ответы:


234

Лучше всего помечать функцию async voidтолько в том случае, если это метод fire и Forgot, если вы хотите ждать, вы должны пометить ее как async Task.

В случае, если вы все еще хотите ждать, оберните его так await Task.Run(() => blah())


бла, очевидно, возвращает задание и имеет асинхронную подпись, почему бы вам позвонить без ожидания. даже VS предупредит вас об этом.
Батмачи

8
await Task.Run(() => An_async_void_method_I_can_not_modify_now())
themefield

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

@JonathanLidbeck заявление «продолжается непосредственно перед мля неправильно попробовать.„Await Task.Run (() => Thread.Sleep (10_000))“, задача ожидается в течение 10 секунд + перед выполнением любой следующей строки
Рохит Шарма

@ 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 и затем установить его в async void, когда вы знаете, что это сделано.

Вы также можете подождать выполнения задачи, которая возвращается из вашей асинхронной пустоты.


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()возвращаемого объекта

Вы не можете действительно функция так не может бытьawaitvoidLoadBlahBlah()void


Я хочу дождаться LoadBlahBlah()окончания, а неblah()
MBZ

10
К сожалению, асинхронные методы void не приостанавливают выполнение (в отличие от асинхронных методов Task)
ghord

-1

Я знаю, что это старый вопрос, но я все еще сталкиваюсь с этой проблемой, и все же нет четкого решения, как правильно сделать это при использовании async / await в методе асинхронной подписи void.

Однако я заметил, что .Wait () работает правильно внутри метода void.

и поскольку async void и void имеют одну и ту же подпись, вам может потребоваться сделать следующее.

void LoadBlahBlah()
{
    blah().Wait(); //this blocks
}

Достаточно запутанно async / await не блокирует следующий код.

async void LoadBlahBlah()
{
    await blah(); //this does not block
}

Когда вы декомпилируете свой код, я предполагаю, что async void создает внутреннюю задачу (так же, как async Task), но поскольку подпись не поддерживает возврат этой внутренней задачи

это означает, что внутренне асинхронный метод void все еще сможет "ожидать" внутренне асинхронных методов. но внешне не может знать, когда внутренняя задача завершена.

Таким образом, я пришел к выводу, что async void работает как задумано, и если вам нужна обратная связь от внутренней Задачи, вам нужно вместо этого использовать подпись асинхронной Задачи.

надеюсь, моя болтовня имеет смысл для всех, кто также ищет ответы.

Изменить: я сделал пример кода и декомпилировал его, чтобы увидеть, что на самом деле происходит.

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 void, если вам нужно знать, когда задача выполнена, для этого и предназначена асинхронная задача.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.