Ok, parece estranho, mas o código é muito simples e explica bem a situação.
public virtual async Task RemoveFromRoleAsync(AzureTableUser user, string role)
{
AssertNotDisposed();
var roles = await GetRolesForUser(user);
roles.Roles = RemoveRoles(roles.Roles, role);
await Run(TableOperation.Replace(roles));
}
(Eu sei que estou falando mais ou menos no resumo abaixo, mas o acima é um método real do que será o código de produção real que está realmente fazendo o que estou perguntando aqui e estou realmente interessado em revisar para correção em relação ao padrão assíncrono / espera.)
Estou encontrando esse padrão cada vez mais agora que estou usando async
/ await
more. O padrão consiste na seguinte cadeia de eventos:
- Aguarde uma ligação inicial que receba algumas informações nas quais preciso trabalhar
- Trabalhe nessas informações de forma síncrona
- Aguarde uma chamada final que salve o trabalho atualizado
O bloco de código acima é geralmente o modo como lidarei com esses métodos. Eu sou await
a primeira chamada, que eu tenho que fazer porque é assíncrona. Em seguida, faço o trabalho necessário, que não é de E / S ou recurso vinculado e, portanto, não é assíncrono. Por fim, guardo meu trabalho, que também é uma async
ligação, e do culto à carga eu await
o faço .
Mas essa é a maneira mais eficiente / correta de lidar com esse padrão? Parece-me que eu poderia pular await
a última ligação, mas e se ela falhar? E devo usar um Task
método como ContinueWith
encadear meu trabalho síncrono com a chamada original? Agora estou em um ponto em que não tenho certeza se estou lidando com isso corretamente.
Dado o código no exemplo , existe uma maneira melhor de lidar com essa cadeia de chamadas de método async / sync / async?
fonte
Respostas:
Sim, acho que é o caminho certo para fazê-lo.
Você não pode pular o segundo
await
. Se você o fizesse, o método pareceria concluir muito cedo (antes da remoção ser realmente concluída) e você nunca descobriria se a remoção falhou.Não vejo como isso
ContinueWith()
ou algo assim ajudaria aqui. Você poderia usá-lo para evitar o usoawait
, mas isso tornaria seu código mais complicado e menos legível. E esse é o objetivoawait
: simplificar a escrita de código assíncrono, quando comparado ao uso de continuações.fonte
A maneira de lidar com esse padrão é garantir que todas as E / S sejam assíncronas. Os métodos de E / S síncrona fazem com que o encadeamento atual seja bloqueado enquanto aguarda uma resposta do destino de E / S (rede, sistema de arquivos etc.).
Outra coisa a considerar é que
await
deve ser usado quando você precisar de um valor de retorno ou quando precisar que o código aguardado seja concluído antes de fazer outra coisa. Se você não precisar de nenhuma dessas coisas, pode "disparar e esquecer" seu método assíncronoTask.Run
.Portanto, para o uso mais eficiente dos recursos de computação, se
RemoveRoles
houver E / S, ele deve se tornarawait RemoveRolesAsync
e os métodos de E / S chamados porRemoveRolesAsync
também devem ser assíncronos (e possivelmente aguardados).Se o desempenho não for sua maior preocupação, é bom executar algumas E / S síncronas em um encadeamento assíncrono. É uma dívida técnica, no entanto. (Neste caso, você pode querer chamar o primeiro método assíncrono com
ConfigureAwait
, dependendo de onde o código está sendo executado.)Aqui está uma visão mais aprofundada das práticas recomendadas - https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
Aqui estão algumas notas sobre o comportamento do ConfigureAwait em diferentes ambientes, como ASP.NET, WebAPI, etc. - /programming/13489065/best-practice-to-call-configureawait-for-all-server-side -código
fonte