Penso che stai confondendo alcune cose, qui. Quello che stai chiedendo è già possibile usando System.Threading.Tasks
, il async
e await
in C # 5 forniranno un po 'più sintetico zucchero sintattico per la stessa funzione.
Usiamo un esempio di Winforms - rilascia un pulsante e una casella di testo sul modulo e utilizza questo codice:
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;
}
Eseguilo e vedrai che (a) non blocca il thread dell'interfaccia utente e (b) non ottieni il solito errore "cross-thread operation not valid" - a meno che non elimini TaskScheduler
argomento dell'ultimo ContinueWith
, nel qual caso lo farai.
Questo è stile di passaggio continuo . La magia si verifica nella classe TaskScheduler
e in particolare l'istanza recuperata da FromCurrentSynchronizationContext
. Passa a questo in qualsiasi continuazione e dici che la continuazione deve essere eseguita su qualsiasi thread chiamato il metodo FromCurrentSynchronizationContext
- in questo caso, il thread dell'interfaccia utente.
Gli spettatori sono leggermente più sofisticati nel senso che sono consapevoli di quale thread hanno iniziato e su quale thread deve continuare la continuazione. Quindi il codice sopra può essere scritto piccolo più 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;
}
Questi due dovrebbero apparire molto simili, e infatti sono molto simili. Il metodo DelayedAddAsync
ora restituisce un Task<int>
invece di un int
, quindi il await
è solo uno schiaffo di continuazioni su ciascuno di questi. La differenza principale è che passa il contesto di sincronizzazione su ogni riga, quindi non devi farlo esplicitamente come nell'ultimo esempio.
In teoria le differenze sono molto più significative. Nel secondo esempio, ogni singola riga nel metodo button1_Click
viene effettivamente eseguita nel thread dell'interfaccia utente, ma l'attività stessa ( DelayedAddAsync
) viene eseguita in background. Nel primo esempio, tutto viene eseguito in background , tranne per l'assegnazione a textBox1.Text
che abbiamo esplicitamente allegato al thread dell'interfaccia utente contesto di sincronizzazione.
Questo è ciò che è veramente interessante su await
- il fatto che un cameriere sia in grado di entrare e uscire dallo stesso metodo senza alcuna chiamata di blocco. Chiama await
, il thread corrente ritorna ai messaggi di elaborazione e, una volta terminato, l'utente attenderà esattamente da dove era stato interrotto, nello stesso thread in cui lo aveva interrotto. Ma in termini di Invoke
/ BeginInvoke
contrasto nella domanda, mi dispiace dire che avresti dovuto smettere di farlo molto tempo fa.