Respostas:
A melhor prática é marcar a função async void
apenas se for o método de ignorar e esquecer; se você quiser aguardar, deve marcá-la como async Task
.
Caso ainda deseje aguardar, envolva-o da seguinte maneira await Task.Run(() => blah())
await Task.Run(() => An_async_void_method_I_can_not_modify_now())
await Task.Run(() => blah())
é enganoso. Isso não espera a conclusão da função assíncrona blah
, apenas a criação (trivial) da tarefa e continua imediatamente antes da blah()
conclusão.
Thread.Sleep
não é assíncrono. Esta questão é sobre aguardar uma async void
função, digamosasync void blah() { Task.Delay(10000); }
Se você pode alterar a assinatura de sua função para, async Task
então você pode usar o código apresentado aqui
A melhor solução é usar async Task
. Você deve evitar async void
por vários motivos, um dos quais é a composição.
Se o método não puder ser retornado Task
(por exemplo, é um manipulador de eventos), você poderá usar SemaphoreSlim
o sinal do método quando ele estiver prestes a sair. Considere fazer isso em um finally
bloco.
faça um AutoResetEvent, chame a função e aguarde AutoResetEvent e defina-o dentro de async void quando você souber que está pronto.
Você também pode aguardar uma tarefa que retorne do seu vazio assíncrono
Você realmente não precisa fazer nada manualmente, a await
palavra-chave pausa a execução da função até blah()
retornar.
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
é o tipo de objeto blah()
retornado
Você não pode realmente await
uma void
função, então LoadBlahBlah()
não pode servoid
LoadBlahBlah()
terminar, nãoblah()
Eu sei que essa é uma pergunta antiga, mas ainda é um problema que eu continuo abordando, e ainda não há uma solução clara para fazer isso corretamente ao usar async / waitit em um método de assinatura assíncrona de void.
No entanto, notei que .Wait () está funcionando corretamente dentro do método void.
e como async void e void têm a mesma assinatura, você pode precisar fazer o seguinte.
void LoadBlahBlah()
{
blah().Wait(); //this blocks
}
Confundidamente, async / waitit não é bloqueado no próximo código.
async void LoadBlahBlah()
{
await blah(); //this does not block
}
Quando você descompila seu código, meu palpite é que o async void cria uma tarefa interna (assim como a tarefa assíncrona), mas como a assinatura não suporta retornar essas tarefas internas
isso significa que internamente o método de async void ainda poderá "aguardar" os métodos de async internamente. mas externamente incapaz de saber quando a tarefa interna está concluída.
Portanto, minha conclusão é que o async void está funcionando conforme o esperado e, se você precisar de feedback da tarefa interna, precisará usar a assinatura da tarefa assíncrona.
espero que minhas divagações façam sentido para quem também procura respostas.
Edit: Eu criei um código de exemplo e o descompilei para ver o que realmente está acontecendo.
static async void Test()
{
await Task.Delay(5000);
}
static async Task TestAsync()
{
await Task.Delay(5000);
}
Se transforma em (editar: eu sei que o código do corpo não está aqui, mas nas máquinas de estado, mas as máquinas de estado eram basicamente idênticas, então eu não me incomodei em adicioná-las)
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;
}
nem o AsyncVoidMethodBuilder nem o AsyncTaskMethodBuilder têm realmente qualquer código no método Start que indique a sua obstrução e sempre será executado de forma assíncrona após o início.
significando sem a tarefa retornada, não haveria maneira de verificar se ela está completa.
conforme o esperado, ele inicia apenas a tarefa executando assíncrona e continua no código. e a tarefa assíncrona, primeiro ela inicia a tarefa e depois a devolve.
então acho que minha resposta seria nunca usar o async void, se você precisar saber quando a tarefa está concluída, é para isso que serve a tarefa assíncrona.