OK, aqui está minha opinião.
Existe algo chamado corotinas que é conhecido há décadas. ("Knuth e Hopper" -classe "por décadas") São generalizações de sub-rotinas , não apenas como elas obtêm e liberam o controle na instrução de início e retorno da função, mas também em pontos específicos ( pontos de suspensão ). Uma sub-rotina é uma corotina sem pontos de suspensão.
Eles são muito fáceis de implementar com macros C, conforme mostrado no artigo a seguir sobre "protothreads". ( http://dunkels.com/adam/dunkels06protothreads.pdf ) Leia. Eu vou esperar...
A conclusão é que as macros criam um grande switche um caserótulo em cada ponto de suspensão. Em cada ponto de suspensão, a função armazena o valor do caserótulo imediatamente a seguir , para que ele saiba onde retomar a execução na próxima vez em que for chamado. E retorna o controle para o chamador.
Isso é feito sem modificar o fluxo aparente de controle do código descrito no "protothread".
Imagine agora que você tem um grande loop chamando todos esses "protothreads" por sua vez e pode executar simultaneamente "protothreads" em um único thread.
Essa abordagem tem duas desvantagens:
- Você não pode manter o estado nas variáveis locais entre as retomadas.
- Você não pode suspender o "protothread" de uma profundidade de chamada arbitrária. (todos os pontos de suspensão devem estar no nível 0)
Existem soluções alternativas para ambos:
- Todas as variáveis locais devem ser puxadas para o contexto do protothread (contexto que já é necessário porque o protothread deve armazenar seu próximo ponto de retomada)
- Se você sentir que realmente precisa chamar outro prototread de um protothread, "crie" um protothread infantil e suspenda até a conclusão da criança.
E se você tivesse suporte do compilador para executar o trabalho de reescrita que as macros e a solução alternativa, bem, você poderia escrever seu código de protothread exatamente como pretendia e inserir pontos de suspensão com uma palavra-chave.
E é isso que asynce awaitsão todos sobre: a criação (Stackless) coroutines.
As corotinas em C # são reificadas como objetos da classe (genérica ou não genérica) Task.
Acho essas palavras-chave muito enganadoras. Minha leitura mental é:
async como "suspensível"
await como "suspender até a conclusão de"
Task como "futuro ..."
Agora. Realmente precisamos marcar a função async? Além de dizer que deve acionar os mecanismos de reescrita do código para tornar a função uma rotina, ele resolve algumas ambiguidades. Considere este código.
public Task<object> AmIACoroutine() {
var tcs = new TaskCompletionSource<object>();
return tcs.Task;
}
Supondo que isso asyncnão seja obrigatório, isso é uma corrotina ou uma função normal? O compilador deve reescrevê-lo como uma rotina ou não? Ambos podem ser possíveis com diferentes semânticas eventuais.