C# Assíncrono – Como funciona?
-
27-09-2019 - |
Pergunta
A Microsoft anunciou o CTP assíncrono do Visual Studio hoje (28 de outubro de 2010) que apresenta o async
e await
palavras-chave em C#/VB para execução de método assíncrono.
Primeiro pensei que o compilador traduzisse as palavras-chave na criação de um thread, mas de acordo com o papel branco e Anders Hejlsberg Apresentação do CDP (às 31h) a operação assíncrona acontece completamente na thread principal.
Como posso executar uma operação em paralelo no mesmo thread?Como isso é tecnicamente possível e para que o recurso é realmente traduzido em IL?
Solução
Funciona de forma semelhante ao yield return
palavra-chave em C# 2.0.
Um método assíncrono não é na verdade um método sequencial comum.Ele é compilado em uma máquina de estados (um objeto) com algum estado (variáveis locais são transformadas em campos do objeto).Cada bloco de código entre dois usos de await
é uma “etapa” da máquina de estado.
Isso significa que quando o método é iniciado, ele apenas executa o primeiro passo e então a máquina de estados retorna e agenda algum trabalho a ser feito - quando o trabalho estiver concluído, ele executará o próximo passo da máquina de estados.Por exemplo este código:
async Task Demo() {
var v1 = foo();
var v2 = await bar();
more(v1, v2);
}
Seria traduzido para algo como:
class _Demo {
int _v1, _v2;
int _state = 0;
Task<int> _await1;
public void Step() {
switch(this._state) {
case 0:
this._v1 = foo();
this._await1 = bar();
// When the async operation completes, it will call this method
this._state = 1;
op.SetContinuation(Step);
case 1:
this._v2 = this._await1.Result; // Get the result of the operation
more(this._v1, this._v2);
}
}
O importante é que ele apenas usa o SetContinuation
para especificar que quando a operação for concluída, ele deve chamar o Step
método novamente (e o método sabe que deve executar o segundo bit do código original usando o _state
campo).Você pode facilmente imaginar que o SetContinuation
seria algo como btn.Click += Step
, que seria executado completamente em um único thread.
O modelo de programação assíncrona em C# é muito próximo dos fluxos de trabalho assíncronos em F# (na verdade, é essencialmente a mesma coisa, exceto alguns detalhes técnicos) e escrever aplicativos GUI reativos de thread único usando async
é uma área bastante interessante - pelo menos penso que sim - veja por exemplo Este artigo (talvez eu devesse escrever uma versão C# agora :-)).
A tradução é semelhante aos iteradores (e yield return
) e, de fato, era possível usar iteradores para implementar programação assíncrona em C# anteriormente.escrevi um artigo sobre isso há algum tempo - e acho que ainda pode lhe dar algumas dicas sobre como funciona a tradução.
Outras dicas
Como posso ter uma operação executada em paralelo no mesmo thread?
Você não pode. A assincronia não é "paralelismo" ou "concorrência". A assincronia pode ser implementada com o paralelismo, ou pode não ser. Pode ser implementado dividindo o trabalho em pequenos pedaços, colocando cada pedaço de trabalho em uma fila e executando cada pedaço de trabalho sempre que o tópico não estiver fazendo mais nada.
Eu tenho toda uma série de artigos no meu blog sobre como tudo isso funciona; O único alemão diretamente para esta pergunta provavelmente subirá na quinta -feira da próxima semana. Ver
http://blogs.msdn.com/b/ericlippert/archive/tags/async/
para detalhes.
Pelo que entendi, o que o async
e await
Palavras -chave fazem é que toda vez que um async
O método emprega o await
Palavra -chave, o compilador transformará o restante do método em uma continuação programada quando a operação assíncrona for concluída. Que permite async
Métodos para retornar ao chamador imediatamente e retomar o trabalho quando a parte assíncrona for concluída.
De acordo com os documentos disponíveis, há muitos detalhes, mas, a menos que eu esteja enganado, essa é a essência disso.
A ver com o objetivo dos métodos assíncronos não é executar muito código em paralelo, mas cortar os métodos assíncronos em vários pequenos pedaços, que podem ser chamados conforme necessário. O ponto principal é que o compilador lidará com toda a fiação complexa de retornos de chamada usando tarefas/continuações. Isso não apenas reduz a complexidade, mas permite que o método Async seja escrito mais ou menos como o código síncrono tradicional.