Usando o CTP assíncrono da Microsoft para .NET, é possível capturar uma exceção lançada por um método assíncrono no método de chamada?
public async void Foo()
{
var x = await DoSomethingAsync();
/* Handle the result, but sometimes an exception might be thrown.
For example, DoSomethingAsync gets data from the network
and the data is invalid... a ProtocolException might be thrown. */
}
public void DoFoo()
{
try
{
Foo();
}
catch (ProtocolException ex)
{
/* The exception will never be caught.
Instead when in debug mode, VS2010 will warn and continue.
The deployed the app will simply crash. */
}
}
Então, basicamente, quero que a exceção do código assíncrono entre no meu código de chamada, se isso for possível.
Respostas:
É um tanto estranho ler, mas sim, a exceção ocorrerá no código de chamada - mas somente se você
await
ouWait()
a chamadaFoo
.Observe que o uso de Wait () pode bloquear o seu aplicativo, se o .Net decidir executar seu método de forma síncrona.
Essa explicação http://www.interact-sw.co.uk/iangblog/2010/11/01/csharp5-async-exceptions é muito boa - discute as etapas que o compilador executa para alcançar essa mágica.
fonte
await
ligar para Foo quando Foo está retornando nulo?async void Foo()
.Type void is not awaitable
?A razão pela qual a exceção não foi detectada é porque o método Foo () tem um tipo de retorno nulo e, portanto, quando aguardar é chamado, ele simplesmente retorna. Como o DoFoo () não está aguardando a conclusão do Foo, o manipulador de exceções não pode ser usado.
Isso abre uma solução mais simples se você pode alterar as assinaturas do método - altere
Foo()
para que ele retorne o tipoTask
e depoisDoFoo()
possaawait Foo()
, como neste código:fonte
Seu código não faz o que você pensa que faz. Os métodos assíncronos retornam imediatamente após o método começar a aguardar o resultado assíncrono. É interessante usar o rastreamento para investigar como o código está realmente se comportando.
O código abaixo faz o seguinte:
Quando você observa os traços
Você notará que o método Run é concluído no thread 2820 enquanto apenas um thread filho foi concluído (2756). Se você colocar um try / catch em torno de seu método wait, poderá "capturar" a exceção da maneira usual, embora seu código seja executado em outro encadeamento quando a tarefa de cálculo for concluída e sua execução for executada.
O método de cálculo rastreia a exceção lançada automaticamente porque eu usei o ApiChange.Api.dll da ferramenta ApiChange . Rastreamento e Refletor ajuda muito a entender o que está acontecendo. Para se livrar do encadeamento, você pode criar suas próprias versões do GetAwaiter BeginAwait e EndAwait e agrupar não uma tarefa, mas, por exemplo, um Lazy e um rastreamento dentro de seus próprios métodos de extensão. Então você entenderá muito melhor o que o compilador e o que o TPL faz.
Agora você vê que não há como tentar / recuperar sua exceção, pois não há um quadro de pilha restante para que nenhuma exceção se propague. Seu código pode estar fazendo algo totalmente diferente depois que você iniciou as operações assíncronas. Pode chamar Thread.Sleep ou até terminar. Enquanto houver um encadeamento em primeiro plano, seu aplicativo continuará felizmente executando tarefas assíncronas.
Você pode manipular a exceção dentro do método assíncrono após a conclusão da operação assíncrona e chamar novamente o thread da interface do usuário. A maneira recomendada de fazer isso é com TaskScheduler.FromSynchronizationContext . Isso funciona apenas se você tiver um thread da interface do usuário e não estiver muito ocupado com outras coisas.
fonte
A exceção pode ser capturada na função assíncrona.
fonte
Também é importante observar que você perderá o rastreamento da pilha cronológica da exceção se tiver um tipo de retorno nulo em um método assíncrono. Eu recomendaria retornar a tarefa da seguinte maneira. Vai facilitar muito a depuração.
fonte
return
instrução, esse código funcionará, poisTask
é retornado "implicitamente" usandoasync / await
.Este blog explica claramente o seu problema Async Best Practices .
A essência disso é que você não deve usar o void como retorno para um método assíncrono, a menos que seja um manipulador de eventos assíncronos, isso é uma prática ruim porque não permite que exceções sejam capturadas ;-).
A melhor prática seria alterar o tipo de retorno para Tarefa. Além disso, tente codificar async completamente, faça todas as chamadas de método assíncrono e seja chamado a partir de métodos assíncronos. Exceto pelo método Main em um console, que não pode ser assíncrono (antes do C # 7.1).
Você encontrará conflitos com aplicativos GUI e ASP.NET se ignorar esta prática recomendada. O impasse ocorre porque esses aplicativos são executados em um contexto que permite apenas um encadeamento e não o abandona ao encadeamento assíncrono. Isso significa que a GUI espera síncrona por um retorno, enquanto o método assíncrono aguarda o contexto: deadlock.
Esse comportamento não acontece em um aplicativo de console, porque é executado no contexto com um pool de threads. O método assíncrono retornará em outro thread que será agendado. É por isso que um aplicativo de console de teste funcionará, mas as mesmas chamadas entrarão em conflito em outros aplicativos ...
fonte