Atualização: o ASP.NET Core não possui umSynchronizationContext
. Se você estiver no ASP.NET Core, não importa se você usa ConfigureAwait(false)
ou não.
Para o ASP.NET "Completo" ou "Clássico" ou o que for, o restante desta resposta ainda se aplica.
Postagem original (para ASP.NET não Core):
Este vídeo da equipe do ASP.NET tem as melhores informações sobre o uso async
no ASP.NET.
Eu tinha lido que é mais eficiente, pois não precisa alternar os contextos de thread para o contexto de thread original.
Isso ocorre com aplicativos de interface do usuário, onde há apenas um thread da interface do usuário no qual você precisa "sincronizar".
No ASP.NET, a situação é um pouco mais complexa. Quando um async
método retoma a execução, ele pega um thread do pool de threads do ASP.NET. Se você desativar a captura de contexto usando ConfigureAwait(false)
, o encadeamento continuará executando o método diretamente. Se você não desativar a captura de contexto, o encadeamento entrará novamente no contexto de solicitação e continuará a executar o método.
Portanto ConfigureAwait(false)
, você não economiza um salto de thread no ASP.NET; ele economiza a reinserção do contexto da solicitação, mas isso normalmente é muito rápido. ConfigureAwait(false)
pode ser útil se você estiver tentando fazer uma pequena quantidade de processamento paralelo de uma solicitação, mas realmente o TPL é mais adequado para a maioria desses cenários.
No entanto, com o ASP.NET Web Api, se sua solicitação estiver chegando em um thread, e você aguardar alguma função e chamar ConfigureAwait (false) que poderia potencialmente colocá-lo em um segmento diferente quando você retornar o resultado final da função ApiController .
Na verdade, apenas fazer um await
pode fazer isso. Depois que seu async
método atinge um await
, o método é bloqueado, mas o thread retorna ao pool de threads. Quando o método está pronto para continuar, qualquer encadeamento é capturado do conjunto de encadeamentos e usado para retomar o método.
A única diferença ConfigureAwait
faz no ASP.NET é se esse thread entra no contexto da solicitação ao retomar o método.
Tenho mais informações básicas no meu artigo do MSDNSynchronizationContext
e na async
publicação do meu blog de introdução .
HttpContext.Current
é transmitido pelo ASP.NETSynchronizationContext
, que é transmitido por padrão quando vocêawait
, mas não é transmitido porContinueWith
. OTOH, o contexto de execução (incluindo restrições de segurança) é o contexto mencionado no CLR via C # e é transmitido por ambosContinueWith
eawait
(mesmo se você usarConfigureAwait(false)
).ConfigureAwait
na verdade, só faz sentido quando você aguarda tarefas , enquantoawait
atua sobre qualquer "aguardável". Outras opções consideradas foram: O comportamento padrão deve descartar o contexto se estiver em uma biblioteca? Ou tem uma configuração de compilador para o comportamento de contexto padrão? Ambos foram rejeitados porque é mais difícil ler o código e dizer o que ele faz.ConfigureAwait(false)
evitar um impasse baseado emResult
/Wait
porque no ASP.NET você não deveria estar usandoResult
/Wait
em primeiro lugar.Resposta breve à sua pergunta: Não. Você não deve ligar
ConfigureAwait(false)
para o nível do aplicativo dessa maneira.Versão TL; DR da resposta longa: se você estiver escrevendo uma biblioteca em que não conhece seu consumidor e não precisa de um contexto de sincronização (que não deveria em uma biblioteca que acredito), sempre use
ConfigureAwait(false)
. Caso contrário, os consumidores da sua biblioteca podem enfrentar conflitos, consumindo seus métodos assíncronos de maneira bloqueadora. Isso depende da situação.Aqui está uma explicação um pouco mais detalhada sobre a importância do
ConfigureAwait
método (uma citação da minha postagem no blog):Além disso, aqui estão dois excelentes artigos para você, exatamente para sua pergunta:
Por fim, há um ótimo vídeo curto de Lucian Wischik exatamente sobre este tópico: Os métodos da biblioteca assíncrona devem considerar o uso de Task.ConfigureAwait (false) .
Espero que isto ajude.
fonte
Task
caminha na pilha para obter oSynchronizationContext
que está errado. OSynchronizationContext
é capturado antes da chamada paraTask
e, em seguida, o restante do código continua noSynchronizationContext
ifSynchronizationContext.Current
não for nulo.SynchronizationContext.Current
esteja claro / ou que a biblioteca seja chamada dentro de um emTask.Run()
vez de precisar escrever em.ConfigureAwait(false)
toda a biblioteca de classes?.ConfigureAwait(false)
s. Talvez fosse mais fácil para os autores da biblioteca se esse fosse o comportamento padrão, mas eu presumo que tornar um pouco mais difícil escrever corretamente uma biblioteca é melhor do que tornar um pouco mais difícil escrever um aplicativo corretamente.A maior desvantagem que encontrei ao usar o ConfigureAwait (false) é que a cultura do encadeamento é revertida para o padrão do sistema. Se você configurou uma cultura, por exemplo ...
e você estiver hospedando em um servidor cuja cultura está definida como en-US, você encontrará antes que ConfigureAwait (false) seja chamado CultureInfo.CurrentCulture retornará en-AU e depois que você for en-US. ie
Se o seu aplicativo estiver fazendo algo que exija formatação de dados específica da cultura, será necessário estar ciente disso ao usar o ConfigureAwait (false).
fonte
ConfigureAwait(false)
sejam usadas.Tenho algumas considerações gerais sobre a implementação de
Task
:using
.ConfigureAwait
foi introduzido no 4.5.Task
foi introduzido no 4.0.Task.ContinueWith
eles não são verdadeiros, foi percebido que a troca de contexto é cara e é desativada por padrão.Eu tenho alguns posts sobre o assunto, mas minha opinião - além da boa resposta de Tugberk - é que você deve ativar todas as APIs assíncronas e idealmente fluir o contexto.Como você está fazendo a assíncrona, você pode simplesmente usar continuações em vez de esperar, para que nenhum impasse seja causado, já que nenhuma espera é feita na biblioteca e você mantém o fluxo para que o contexto seja preservado (como HttpContext).
O problema é quando uma biblioteca expõe uma API síncrona, mas usa outra API assíncrona - portanto, você precisa usar
Wait()
/Result
no seu código.fonte
Task.Dispose
se quiser; você simplesmente não precisa na grande maioria das vezes. 2)Task
foi introduzido no .NET 4.0 como parte do TPL, o que não era necessárioConfigureAwait
; quandoasync
foi adicionado, eles reutilizaram oTask
tipo existente em vez de inventar um novoFuture
.Task
s; o "contexto" controlado porContinueWith
é umSynchronizationContext
ouTaskScheduler
. Esses diferentes contextos são explicados em detalhes no blog de Stephen Toub .Thread
s, mas que não flua mais comContinueWith()
), isso torna sua resposta confusa de ler.