Oi Renan, tudo bem?
O Task.Run
é implementado com a mesma lógica usada para Task.Factory.StartNew
. A diferença é que ele já passa alguns parâmetros default.
A instrução:
Task.Run(acao);
equivale a:
Task.Factory.StartNew(acao,
CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
Sobre sua segunda dúvida:
Digamos que você utilize o operador await
num método assíncrono MeuMetodo()
de uma aplicação windows forms.
public async Task<string> MeuMetodo()
{
using (var resposta = await metodoABC())
{
return await resposta.MetodoXYZ();
}
}
Um outro método MeuChamador()
normalmente invocaria o MeuMétodo
, de forma assíncrona, assim:
var resultado = await MeuMetodo();
Isso não provoca nenhum problema. Porém, se o chamador fizer uma chamada síncrona, tentando acessar a propriedade Result
...
var resultado = MeuMetodo().Result;
Isso provocará um deadlock. Esse deadlock acontece porque numa aplicação Windows Forms existe uma thread principal (de interface de usuário). Quando você usa um método assíncrono, ele tenta "sincronizar" o contexto, rodando o método no contexto da thread da interface do usuário (thread principal). Como o MeuMetodo() ainda não terminou, também não é possível acessar a propriedade Result, causando um deadlock.
O método ConfigureAwait(false)
configura a tarefa para que a continuação após o await
não tenha que ser executada no contexto do chamador, evitando possíveis deadlocks ("bloqueios mortais" na thread). Com .ConfigureAwait(false)
, o código pode rodar numa outra thread (do thread pool) e assim evitamos o deadlock.
Por isso, você deve usar ConfigureAwait(false)
em MeuMetodo()
, para evitar que as chamadas síncronas provoquem deadlock na sua aplicação:
public async Task<string> MeuMetodo()
{
using (var resposta = await metodoABC().ConfigureAwait(false))
{
return await resposta.MetodoXYZ().ConfigureAwait(false);
}
}