Penso che stai confondendo alcune cose, qui. Quello che stai chiedendo è già possibile utilizzare System.Threading.Tasks
, async
e await
in C # 5 forniranno solo un po 'di zucchero sintattico più carino per la stessa funzione.
Facciamo un esempio di Winforms: rilascia un pulsante e una casella di testo sul modulo e usa 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 "operazione cross-thread non valida" - a meno che tu non rimuova l' TaskScheduler
argomento dall'ultimo ContinueWith
, in quale caso lo farai.
Questo è lo stile di passaggio di continuazione standard . La magia avviene in TaskScheduler
classe e in particolare l'istanza recuperata da FromCurrentSynchronizationContext
. Passalo in qualsiasi continuazione e dici che la continuazione deve essere eseguita su qualsiasi thread chiamato FromCurrentSynchronizationContext
metodo - 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 avvenire la continuazione. Quindi il codice sopra può essere scritto un po ' 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 in effetti sono molto simili. Il DelayedAddAsync
metodo ora restituisce un Task<int>
invece di un int
, e quindi await
sta semplicemente schiaffeggiando continuazioni su ognuna di quelle. La differenza principale è che passa lungo il contesto di sincronizzazione su ogni riga, quindi non è necessario farlo esplicitamente come abbiamo fatto nell'ultimo esempio.
In teoria le differenze sono molto più significative. Nel secondo esempio, ogni singola riga del button1_Click
metodo 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 l'assegnazione a textBox1.Text
cui abbiamo esplicitamente associato il contesto di sincronizzazione del thread dell'interfaccia utente.
Questo è ciò che è davvero interessante await
: il fatto che un cameriere è in grado di entrare e uscire dallo stesso metodo senza bloccare le chiamate. Chiama await
, il thread corrente ritorna all'elaborazione dei messaggi e, quando è finito, il cameriere riprenderà esattamente da dove era stato interrotto, nello stesso thread in cui era stato interrotto. Ma in termini di Invoke
/ BeginInvoke
contrasto nella domanda, io ' Mi dispiace dire che avresti dovuto smettere di farlo molto tempo fa.
await
funzionalità. È solo un sacco di zucchero sintattico per il passaggio di seguito . Forse ci sono altri miglioramenti non correlati a WinForms che dovrebbero aiutare? Ciò rientrerebbe tuttavia nel framework .NET stesso e non nello specifico C #.