Desfocando as linhas entre funções assíncronas e regulares no C # 5.0

10

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 asynca 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:

  1. As funções assíncronas não exigirão mais a asyncdeclaração. Seus tipos de retorno não precisariam ser envolvidos Task<>. 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.

  2. 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.

  3. 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.

  4. 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).

  5. 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 awaitchamada 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

talkol
fonte
3
Você sempre espera suas operações assíncronas? Por favor me diga que você não faça isso imediatamente após a queima-los ...
Jimmy Hoffa
11
Além disso, uma das grandes coisas do assíncrono é que você não precisa fazer isso awaitimediatamente. Você pode fazer algo parecido var task = FooAsync(); Bar(); await task;. Como eu faria isso na sua proposta?
svick
3
Então está tendo uma discussão? Onde está a minha BFG-3000 ...
Robert Harvey
2
@talkol Você acha que a programação paralela é obscena? Essa é uma perspectiva interessante, para dizer o mínimo, quando você está falando async. Eu acho que é uma das grandes vantagens de async- 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: é chamada await.
svick
11
@ Rick haha, agora nós fomos lá :) Eu não acho que prog paralelo é obsceno, mas acho que fazê-lo com async-waitit é. A espera assíncrona é o açúcar sintático para manter seu estado de espírito síncrono sem pagar o preço do bloqueio. Se você já está pensando em paralelo, peço-lhe para usar um padrão diferente
talkol

Respostas:

9

Sua pergunta já foi respondida na pergunta SO que você vinculou.

O objetivo do async / waitit é facilitar a gravação de código em um mundo com muitas operações de alta latência. A grande maioria de suas operações não possui alta latência.

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 asynce awaité 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.

Robert Harvey
fonte
Observe a diferença substancial entre a questão SO e esta. O SO pergunta por que não fazer tudo assíncrono. Aqui não sugerimos isso, sugerimos fazer 10% de assíncrona, apenas usando a mesma sintaxe para isso. Usando uma sintaxe mais próxima tem a vantagem de que você pode facilmente mudar quais 10% é assíncrona, sem efeitos sofrimento dominó dessas mudanças
talkol
Não sei ao certo por asyncque produziria infestações por zumbis. Mesmo que um método chame 10 outros métodos, você não precisa apenas do método asyncde nível superior?
Robert Harvey
6
Digamos que 100% do meu código atual seja sincronizado. Agora eu tenho uma única função interna de nível de folha que consulta o banco de dados que eu gostaria de alterar para assíncrono. Agora, para torná-lo assíncrono, preciso que o chamador seja assíncrono e que o chamador seja assíncrono, e assim por diante até o nível superior. É claro que eu estou falando sobre o caso em toda a cadeia é aguardado em cima (para manter o projeto síncrono do código ou passar vales de retorno)
talkol