Eu tenho um async
método:
public async Task<string> GenerateCodeAsync()
{
string code = await GenerateCodeService.GenerateCodeAsync();
return code;
}
Eu preciso chamar esse método a partir de um método síncrono.
Como fazer isso sem precisar duplicar o GenerateCodeAsync
método para que ele funcione de forma síncrona?
Atualizar
No entanto, nenhuma solução razoável foi encontrada.
No entanto, vejo que HttpClient
já implementa esse padrão
using (HttpClient client = new HttpClient())
{
// async
HttpResponseMessage responseAsync = await client.GetAsync(url);
// sync
HttpResponseMessage responseSync = client.GetAsync(url).Result;
}
Respostas:
Você pode acessar a
Result
propriedade da tarefa, que fará com que seu encadeamento seja bloqueado até que o resultado esteja disponível:Nota: Em alguns casos, isso pode levar a um impasse: Sua chamada para
Result
bloquear o encadeamento principal, impedindo a execução do restante do código assíncrono. Você tem as seguintes opções para garantir que isso não aconteça:.ConfigureAwait(false)
ao seu método de biblioteca ouexecute explicitamente seu método assíncrono em um thread do pool de threads e aguarde o término:
Isso não significa que você deve adicionar sem pensar
.ConfigureAwait(false)
depois de todas as chamadas assíncronas! Para uma análise detalhada sobre por que e quando você deve usar.ConfigureAwait(false)
, consulte a seguinte postagem no blog:fonte
result
um conflito, então quando é seguro obter o resultado? Toda chamada assíncrona exigeTask.Run
ouConfigureAwait(false)
?AspNetSynchronizationContext.Post
Serializa assíncrona continuações:Task newTask = _lastScheduledTask.ContinueWith(_ => SafeWrapCallback(action)); _lastScheduledTask = newTask;
Task.Run
para se manter seguro. Ou use algo comoWithNoContext
para reduzir a troca de threads redundantes..Result
ainda podem entrar em conflito se o chamador estiver no próprio pool de threads. Veja um cenário em que o Pool de Threads tem o tamanho 32 e 32 tarefas estão em execução eWait()/Result
aguardando uma 33ª tarefa ainda a ser agendada que deseja executar em um dos threads em espera.Você deve obter o waititer (
GetAwaiter()
) e encerrar a espera pela conclusão da tarefa assíncrona (GetResult()
).fonte
Task.GetAwaiter
: este método é destinado ao uso do compilador, e não ao código do aplicativo.Você deve conseguir fazer isso usando delegados, expressão lambda
fonte
É possível com
GenerateCodeAsync().Result
ouGenerateCodeAsync().Wait()
, como a outra resposta sugere. Isso bloquearia o encadeamento atual até aGenerateCodeAsync
conclusão.No entanto, sua pergunta está marcada com asp.nete você também deixou o comentário:
Meu argumento é que você não deve estar bloqueando um método assíncrono no ASP.NET. Isso reduzirá a escalabilidade do seu aplicativo Web e poderá criar um impasse (quando uma
await
continuação internaGenerateCodeAsync
for postada emAspNetSynchronizationContext
). UsarTask.Run(...).Result
para transferir algo para um encadeamento de pool e, em seguida, bloquear prejudicará ainda mais a escalabilidade, pois incorre em mais um encadeamento para processar uma determinada solicitação HTTP.O ASP.NET possui suporte interno para métodos assíncronos, por meio de controladores assíncronos (no ASP.NET MVC e na Web API) ou diretamente via
AsyncManager
ePageAsyncTask
no ASP.NET clássico. Você deveria usá-lo. Para mais detalhes, verifique esta resposta .fonte
SaveChanges()
método deDbContext
, e aqui estou chamando os métodos assíncronos, por isso, infelizmente assíncrona controlador não vai me ajudar nesta situaçãoSaveChangesAsync
eSaveChanges
apenas garantir que eles não sejam chamados de ambos no mesmo projeto do ASP.NET..NET MVC
filtros apoiar código assíncrono, por exemploIAuthorizationFilter
, por isso não posso usarasync
todo o caminhoO Microsoft Identity possui métodos de extensão que chamam métodos assíncronos de forma síncrona. Por exemplo, existe o método GenerateUserIdentityAsync () e igual CreateIdentity ()
Se você olhar para UserManagerExtensions.CreateIdentity (), será assim:
Agora vamos ver o que o AsyncHelper.RunSync faz
Portanto, este é o seu invólucro para o método assíncrono. E por favor, não leia os dados do Result - ele potencialmente bloqueará seu código no ASP.
Existe outra maneira - que é suspeita para mim, mas você pode considerar isso também
fonte
Para evitar conflitos, sempre tento usar
Task.Run()
quando preciso chamar um método assíncrono de forma síncrona mencionada pelo @Heinzi.No entanto, o método precisa ser modificado se o método assíncrono usar parâmetros. Por exemplo,
Task.Run(GenerateCodeAsync("test")).Result
dá o erro:Isso poderia ser chamado assim:
fonte
A maioria das respostas neste segmento é complexa ou resultará em impasse.
O método a seguir é simples e evitará impasse, pois aguardamos a conclusão da tarefa e só então obtemos o resultado -
Além disso, aqui está uma referência ao artigo do MSDN que fala exatamente da mesma coisa - https://blogs.msdn.microsoft.com/jpsanders/2017/08/28/asp-net-do-not-use-task-result- no contexto principal /
fonte
Eu prefiro uma abordagem sem bloqueio:
fonte
Bem, eu estou usando esta abordagem:
fonte
A outra maneira poderia ser se você quiser esperar até que a tarefa seja concluída:
fonte
EDITAR:
Task possui o método Wait, Task.Wait (), que aguarda a "promessa" resolver e, em seguida, continua, tornando-o síncrono. exemplo:
fonte
Se você tiver um método assíncrono chamado " RefreshList ", poderá chamar esse método assíncrono a partir de um método não assíncrono, como abaixo.
fonte