Je pense que vous confondez quelques choses ici. Ce que vous demandez est déjà possible à l'aide System.Threading.Tasks
, async
et await
en C # 5 vont juste fournir un peu plus de sucre syntaxique pour la même fonctionnalité.
Prenons un exemple Winforms - déposez un bouton et une zone de texte sur le formulaire et utilisez ce code:
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;
}
Exécutez-le et vous verrez que (a) il ne bloque pas le thread d'interface utilisateur et (b) vous n'obtenez pas l'erreur habituelle "opération cross-thread non valide" - sauf si vous supprimez l' TaskScheduler
argument du dernier ContinueWith
, dans auquel cas vous le ferez.
C'est standard style de passage de continuation . La magie opère dans la TaskScheduler
classe et plus précisément dans l'instance récupérée par FromCurrentSynchronizationContext
. Passez cela dans n'importe quelle continuation et vous lui dites que la continuation doit s'exécuter sur le thread appelé la FromCurrentSynchronizationContext
méthode - dans ce cas, le thread d'interface utilisateur.
Les attente sont légèrement plus sophistiquées dans le sens où elles savent sur quel thread elles ont commencé et sur quel thread la suite doit se produire. Le code ci-dessus peut donc être écrit un peu plus naturellement:
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;
}
Ces deux devraient être très similaires, et en fait, ils sont très similaires. leDelayedAddAsync
méthode retourne maintenant un Task<int>
au lieu d'un int
, et donc le await
fait de simplement gifler les continuations sur chacun d'eux. La principale différence est qu'elle passe le long du contexte de synchronisation sur chaque ligne, vous n'avez donc pas à le faire explicitement comme nous l'avons fait dans le dernier exemple.
En théorie, les différences sont beaucoup plus importantes. Dans le deuxième exemple, chaque ligne de la button1_Click
méthode est réellement exécutée dans le thread d'interface utilisateur, mais la tâche elle-même ( DelayedAddAsync
) s'exécute en arrière-plan. Dans le premier exemple, tout s'exécute en arrière - plan , à l' exception de l'affectation à textBox1.Text
laquelle nous avons explicitement attaché le contexte de synchronisation du thread d'interface utilisateur.
C'est ce qui est vraiment intéressant await
- le fait qu'un serveur d'attente peut sauter dans et hors de la même méthode sans aucun appel bloquant. Vous appelez await
, le thread actuel revient au traitement des messages, et quand c'est fait, le serveur reprendra exactement là où il s'était arrêté, dans le même thread qu'il a laissé. Mais en termes de votre Invoke
/ BeginInvoke
contraste dans la question, je '' Je suis désolé de dire que vous auriez dû arrêter cela il y a longtemps.
await
fonctionnalité. C'est juste beaucoup de sucre syntaxique pour passer la suite . Peut-être y a-t-il d'autres améliorations non liées à WinForms qui sont censées aider? Cela tomberait dans le cadre du .NET lui-même, et non en C # en particulier.