Em este artigo MSDN , o código seguinte exemplo é fornecido (ligeiramente editado por brevidade):
public async Task<ActionResult> Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Department department = await db.Departments.FindAsync(id);
if (department == null)
{
return HttpNotFound();
}
return View(department);
}
O FindAsync
método recupera um Department
objeto por seu ID e retorna a Task<Department>
. Em seguida, o departamento é imediatamente verificado para ver se é nulo. Pelo que entendi, solicitar o valor da tarefa dessa maneira bloqueará a execução do código até que o valor do método esperado seja retornado, efetivamente fazendo desta uma chamada síncrona.
Por que você faria isso? Não seria mais simples chamar o método síncrono Find(id)
, se você vai bloquear imediatamente?
c#
.net
asp.net-mvc
async
Robert Harvey
fonte
fonte
... else return null;
Em seguida, você precisa verificar se o método realmente encontrou o departamento solicitado.Respostas:
Não é bem assim.
Quando você chama,
await db.Departments.FindAsync(id)
a tarefa é enviada e o encadeamento atual é retornado ao pool para uso por outras operações. O fluxo de execução é bloqueado (como seria independentemente do usodepartment
logo após, se eu entendi as coisas corretamente), mas o encadeamento em si é livre para ser usado por outras coisas enquanto você espera pela conclusão da operação fora da máquina (e sinalizado por uma porta de evento ou conclusão).Se você ligou
d.Departments.Find(id)
, o encadeamento fica lá e aguarda a resposta, mesmo que a maior parte do processamento esteja sendo feita no banco de dados.Você está efetivamente liberando recursos da CPU quando vinculado ao disco.
fonte
await
fiz foi assinar o restante do método como uma continuação no mesmo thread (há exceções; alguns métodos assíncronos ativam seu próprio thread) ou assinar oasync
método como uma continuação no mesmo thread e permitir que o código restante a ser executado (como você pode ver, não sou muito claro comoasync
funciona). O que você está descrevendo soa mais como uma forma sofisticada deThread.Sleep(untilReturnValueAvailable)
ConfigureAwait
iirc).await
ser chamadapublic async Task<ActionResult> Details(int? id)
. Caso contrário, a chamada original será bloqueada, aguardandodepartment == null
a resolução.await ...
"retorna", aFindAsync
chamada termina. Isso é o que a espera faz. É chamado de aguardar porque faz com que seu código aguarde as coisas. (Mas note que não é o mesmo que fazer a espera thread atual para coisas)Eu realmente odeio que nenhum dos exemplos mostre como é possível esperar algumas linhas antes de aguardar a tarefa. Considere isto.
Esse é o tipo de código que os exemplos incentivam, e você está certo. Há pouco sentido nisso. Ele libera o thread principal para fazer outras coisas, como responder à entrada da interface do usuário, mas o verdadeiro poder do async / waitit é que eu posso continuar fazendo outras coisas facilmente enquanto aguardo a conclusão de uma tarefa potencialmente demorada. O código acima "bloqueará" e espere para executar a linha de impressão até obtermos o Foo & Bar. Não há necessidade de esperar embora. Podemos processar isso enquanto esperamos.
Agora, com o código reescrito, não paramos e aguardamos nossos valores até que seja necessário. Estou sempre atento a esse tipo de oportunidade. Ser esperto quando aguardamos pode levar a melhorias significativas no desempenho. Hoje em dia, temos vários núcleos, e podemos usá-los.
fonte
await
e deixe o thread fazer coisas completamente diferentes.Então, há mais o que acontece nos bastidores aqui. Async / Await é açúcar sintático. Primeiro, observe a assinatura da função FindAsync. Retorna uma tarefa. Já está vendo a mágica da palavra-chave, ela descompacta essa tarefa em um departamento.
A função de chamada não é bloqueada. O que acontece é que a atribuição ao departamento e tudo o que segue a palavra-chave wait são encerradas e, para todos os efeitos, são passadas para o método Task.ContinueWith (a função FindAsync é executada automaticamente em um thread diferente).
É claro que há mais o que acontece nos bastidores, porque a operação é reorganizada no encadeamento original (para que você não precise mais se preocupar em sincronizar com a interface do usuário ao executar uma operação em segundo plano) e no caso de a função de chamada ser Async ( e sendo chamado de forma assíncrona) a mesma coisa acontece na pilha.
Então o que acontece é que você adquire a magia das operações assíncronas, sem as armadilhas.
fonte
Não, não está retornando imediatamente. A espera torna o método chamado assíncrono. Quando FindAsync é chamado, o método Details retorna com uma tarefa que não está concluída. Quando o FindAsync terminar, ele retornará seu resultado na variável de departamento e retomará o restante do método Detalhes.
fonte
async
await
geralmente não cria novos encadeamentos e, mesmo que tenha criado, você ainda precisa aguardar que o valor do departamento descubra se é nulo.public async Task<ActionResult>
também deve serawait
atendida.await
não deve ser misturado com.Wait()
ou.Result
, pois isso pode causar conflitos. A cadeia async / waitit geralmente termina em uma função com umaasync void
assinatura, usada principalmente para manipuladores de eventos ou para funções invocadas diretamente pelos elementos da interface do usuário.Eu gosto de pensar em "assíncrono" como um contrato, um contrato que diz "eu posso executar isso de forma assíncrona se você precisar, mas você pode me chamar como qualquer outra função síncrona".
Ou seja, um desenvolvedor estava fazendo funções e algumas decisões de design os levaram a fazer / marcar várias funções como "assíncronas". O chamador / consumidor das funções tem a liberdade de usá-las como quiserem. Como você diz, você pode chamar a chamada em espera imediatamente antes da chamada da função e aguardá-la. Dessa forma, você a tratou como uma função síncrona, mas se você quiser, pode chamá-la sem aguardar como
e depois, digamos, 10 linhas abaixo da função que você chama
portanto, tratando-o como uma função assíncrona.
Você decide.
fonte
"se você vai bloquear imediatamente de qualquer maneira" a resposta é "Sim". Somente quando você precisar de uma resposta rápida, aguardar / assíncrono fará sentido. Por exemplo, o encadeamento da interface do usuário chega a um método realmente assíncrono, o encadeamento da interface do usuário retornará e continuará a ouvir o clique do botão, enquanto o código abaixo de "aguardar" será excutado por outro encadeamento e, finalmente, obterá o resultado.
fonte