A programação assíncrona "cresce" através da base de código. Foi comparado a um vírus zumbi . A melhor solução é permitir que ela cresça, mas às vezes isso não é possível.
Escrevi alguns tipos na minha biblioteca Nito.AsyncEx para lidar com uma base de código parcialmente assíncrona. Porém, não há solução que funcione em todas as situações.
Solução A
Se você possui um método assíncrono simples que não precisa ser sincronizado de volta ao seu contexto, pode usar Task.WaitAndUnwrapException:
var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();
Você não deseja usar Task.Waitou Task.Resultporque eles envolvem exceções AggregateException.
Esta solução é apropriada apenas se MyAsyncMethodnão for sincronizada de volta ao seu contexto. Em outras palavras, todo awaitem MyAsyncMethoddeve terminar com ConfigureAwait(false). Isso significa que não é possível atualizar nenhum elemento da interface do usuário nem acessar o contexto de solicitação do ASP.NET.
Solução B
Se MyAsyncMethodprecisar sincronizar de volta ao seu contexto, você poderá usar AsyncContext.RunTaskpara fornecer um contexto aninhado:
var result = AsyncContext.RunTask(MyAsyncMethod).Result;
* Atualização 14/04/2014: nas versões mais recentes da biblioteca, a API é a seguinte:
var result = AsyncContext.Run(MyAsyncMethod);
(Não há problema em usar Task.Resultneste exemplo, porque RunTaskpropagará Taskexceções).
O motivo pelo qual você pode precisar, em AsyncContext.RunTaskvez de, Task.WaitAndUnwrapExceptioné por causa de uma possibilidade bastante sutil de conflito que ocorre no WinForms / WPF / SL / ASP.NET:
- Um método síncrono chama um método assíncrono, obtendo a
Task.
- O método síncrono faz uma espera de bloqueio no
Task.
- O
asyncmétodo usa awaitsem ConfigureAwait.
- O
Tasknão pode ser concluído nessa situação porque ele é concluído apenas quando o asyncmétodo é concluído; o asyncmétodo não pode ser concluído porque está tentando agendar sua continuação para SynchronizationContext, e WinForms / WPF / SL / ASP.NET não permitirá que a continuação seja executada porque o método síncrono já está sendo executado nesse contexto.
Essa é uma das razões pelas quais é uma boa ideia usar o máximo possível ConfigureAwait(false)em todos os asyncmétodos.
Solução C
AsyncContext.RunTasknão funcionará em todos os cenários. Por exemplo, se o asyncmétodo aguardar algo que exija a conclusão de um evento da interface do usuário, você entrará em conflito mesmo com o contexto aninhado. Nesse caso, você pode iniciar o asyncmétodo no pool de threads:
var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();
No entanto, esta solução requer um MyAsyncMethodque funcione no contexto do conjunto de encadeamentos. Portanto, ele não pode atualizar elementos da interface do usuário nem acessar o contexto de solicitação do ASP.NET. E, nesse caso, você também pode adicionar ConfigureAwait(false)suas awaitdeclarações e usar a solução A.
Atualização, 01/05/2019: As "práticas menos recomendadas" atuais estão em um artigo do MSDN aqui .