Acho que você está confundindo algumas coisas aqui. O que você está pedindo é já possível usando System.Threading.Tasks
, o async
e await
em C # 5 está indo só para proporcionar um pouco de açúcar sintático mais agradável para o mesmo recurso.
Vamos usar um exemplo do Winforms - solte um botão e uma caixa de texto no formulário e use este código:
private void button1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew<int>(() => DelayedAdd(5, 10))
.ContinueWith(t => DelayedAdd(t.Result, 20))
.ContinueWith(t => DelayedAdd(t.Result, 30))
.ContinueWith(t => DelayedAdd(t.Result, 50))
.ContinueWith(t => textBox1.Text = t.Result.ToString(),
TaskScheduler.FromCurrentSynchronizationContext());
}
private int DelayedAdd(int a, int b)
{
Thread.Sleep(500);
return a + b;
}
Execute-o e você verá que (a) ele não bloqueia o encadeamento da interface do usuário e (b) você não recebe o erro "operação de encadeamento inválida" usual - a menos que você remova o TaskScheduler
argumento do último ContinueWith
, em Nesse caso, você irá.
Este é o padrão do pântano estilo de passagem de continuação . A mágica acontece na TaskScheduler
classe e, especificamente, na instância recuperada por FromCurrentSynchronizationContext
. Passe isso para qualquer continuação e você diz que a continuação deve ser executada em qualquer thread chamado FromCurrentSynchronizationContext
método - nesse caso, o thread da interface do usuário.
Os garçons são um pouco mais sofisticados, no sentido de saberem em qual encadeamento iniciaram e em qual encadeamento a continuação precisa ocorrer. Portanto, o código acima pode ser escrito um pouco mais naturalmente:
private async void button1_Click(object sender, EventArgs e)
{
int a = await DelayedAddAsync(5, 10);
int b = await DelayedAddAsync(a, 20);
int c = await DelayedAddAsync(b, 30);
int d = await DelayedAddAsync(c, 50);
textBox1.Text = d.ToString();
}
private async Task<int> DelayedAddAsync(int a, int b)
{
Thread.Sleep(500);
return a + b;
}
Esses dois devem parecer muito semelhantes e, de fato, são muito semelhantes. oDelayedAddAsync
método agora retorna um em Task<int>
vez de um int
e, portanto, await
está apenas colocando continuações em cada uma delas. A principal diferença é que ela está transmitindo o contexto de sincronização em cada linha, para que você não precise fazer isso explicitamente, como fizemos no último exemplo.
Em teoria, as diferenças são muito mais significativas. No segundo exemplo, todas as linhas do button1_Click
método são realmente executadas no thread da interface do usuário, mas a própria tarefa ( DelayedAddAsync
) é executada em segundo plano. No primeiro exemplo, tudo é executado em segundo plano , exceto a atribuição à textBox1.Text
qual anexamos explicitamente o contexto de sincronização do thread da interface do usuário.
É disso que realmente interessa await
- o fato de um garçom ser capaz de entrar e sair do mesmo método sem nenhuma chamada de bloqueio. Você liga await
, o encadeamento atual volta ao processamento de mensagens e, quando terminar, o garçom retoma exatamente onde parou, no mesmo encadeamento em que parou. Mas, em termos de seu Invoke
/ BeginInvoke
contraste na pergunta, eu ' Lamento dizer que você deveria ter parado de fazer isso há muito tempo.
await
funcionalidade. É apenas muito açúcar sintático para a passagem contínua . Possivelmente, existem outras melhorias não relacionadas ao WinForms que devem ajudar? Isso seria abrangido pelo próprio framework .NET, e não pelo C # especificamente.