Ultimamente, não consigo me cansar do incrível padrão de espera em assíncrona do C # 5.0. Onde você esteve por toda a minha vida?
Estou absolutamente empolgado com a sintaxe simples, mas estou tendo uma pequena dificuldade. Meu problema é que as funções assíncronas têm uma declaração totalmente diferente das funções regulares. Como apenas funções assíncronas podem aguardar em outras funções assíncronas, quando estou tentando portar algum código de bloqueio antigo para assíncrono, estou tendo um efeito dominó de funções que preciso converter.
As pessoas têm se referido a isso como uma infestação de zumbis . Quando o assíncrono recebe uma mordida no seu código, ele fica cada vez maior. O processo de portar não é difícil, é apenas lançar async
a declaração e agrupar o valor de retorno Task<>
. Mas é irritante fazer isso repetidamente ao transportar código síncrono antigo.
Parece-me que será muito mais natural se ambos os tipos de função (assíncrona e simples sincronização antiga) tiverem exatamente a mesma sintaxe. Se esse fosse o caso, a portabilidade não requer esforço e eu poderia alternar sem dor entre as duas formas.
Eu acho que isso poderia funcionar se seguirmos estas regras:
As funções assíncronas não exigirão mais a
async
declaração. Seus tipos de retorno não precisariam ser envolvidosTask<>
. O compilador identificará uma função assíncrona durante a compilação por si só e executará o agrupamento de tarefas <> automaticamente conforme necessário.Não há mais chamadas de disparar e esquecer para funções assíncronas. Se você deseja chamar uma função assíncrona, precisará aguardá-la. Eu quase não uso fogo e esqueça de qualquer maneira, e todos os exemplos de condições de corrida ou impasses loucos sempre parecem se basear neles. Eu acho que eles são muito confusos e "fora de contato" com a mentalidade síncrona que tentamos aproveitar.
Se você realmente não pode viver sem disparar e esquecer, haverá uma sintaxe especial para isso. De qualquer forma, não fará parte da sintaxe unificada simples da qual estou falando.
A única palavra-chave necessária para indicar uma chamada assíncrona é
await
. Se você aguardar, a chamada é assíncrona. Caso contrário, a chamada é antiga e síncrona (lembre-se, não temos mais o recurso de ignorar).O compilador identificará funções assíncronas automaticamente (já que elas não possuem mais uma declaração especial). A regra 4 torna isso muito simples de executar - se uma função tem uma
await
chamada interna, é assíncrona.
Isso poderia funcionar? Ou eu estou esquecendo de alguma coisa? Essa sintaxe unificada é muito mais fluida e pode resolver completamente a infestação de zumbis.
Alguns exemplos:
// assume this is an async function (has await calls inside)
int CalcRemoteBalanceAsync() { ... }
// assume this is a regular sync function (has no await calls inside)
int CalcRemoteBalance() { ... }
// now let's try all combinations and see what they do:
// this is the common synchronous case - it will block
int b1 = CalcRemoteBalance();
// this is the common asynchronous case - it will not block
int b2 = await CalcRemoteBalanceAsync();
// choosing to call an asynchronous function in a synchronous manner - it will block
// this syntax was used previously for async fire-and-forget, but now it's just synchronous
int b3 = CalcRemoteBalanceAsync();
// strange combination - it will block since it's calling a synchronous function
// it should probably show a compilation warning though
int b4 = await CalcRemoteBalance();
Nota: esta é uma continuação de uma discussão relacionada interessante no SO
fonte
await
imediatamente. Você pode fazer algo parecidovar task = FooAsync(); Bar(); await task;
. Como eu faria isso na sua proposta?async
. Eu acho que é uma das grandes vantagens deasync
-await
: que ele permite que você facilmente compor operações assíncronas (e não apenas no mais simples "start A, esperar por um, comece B, espera para B" caminho). E já existe uma sintaxe especial exatamente para esse fim: é chamadaawait
.Respostas:
Sua pergunta já foi respondida na pergunta SO que você vinculou.
Quando o WinRT foi lançado, os designers estavam descrevendo como eles decidiram quais operações seriam assíncronas. Eles decidiram que qualquer coisa que levasse 50ms ou mais seria assíncrona e o restante dos métodos seriam métodos comuns e não assíncronos.
Quantos dos métodos tiveram que ser reescritos para torná-los assíncronos? Cerca de 10% deles. Os outros 90% não foram afetados.
Eric Lippert continua explicando em detalhes técnicos bastante substanciais por que eles optaram por não adotar uma abordagem de tamanho único. Ele basicamente diz isso
async
eawait
é uma implementação parcial do estilo de passagem de continuação, e que otimizar esse estilo para atender a todos os casos é um problema difícil.fonte
async
que produziria infestações por zumbis. Mesmo que um método chame 10 outros métodos, você não precisa apenas do métodoasync
de nível superior?