web-dev-qa-db-fra.com

Mises à jour de Task.Run et de l'interface utilisateur

Cet extrait de code provient de le blog de Stephen Cleary et donne un exemple de la façon de signaler les progrès lors de l'utilisation de Task.Run. Je voudrais savoir pourquoi il n'y a pas de problèmes croisés avec la mise à jour de l'interface utilisateur, c'est-à-dire pourquoi l'invocation n'est pas requise?

private async void button2_Click(object sender, EventArgs e)
{
    var progressHandler = new Progress<string>(value =>
    {
        label2.Text = value;
    });
    var progress = progressHandler as IProgress<string>;
    await Task.Run(() =>
    {
        for (int i = 0; i != 100; ++i)
        {
            if (progress != null)
                progress.Report("Stage " + i);
            Thread.Sleep(100);
        }
    });
    label2.Text = "Completed.";
}
20
Alex Strickland

Progress<T> capture le SynchronisationContext courant lorsqu'il est instancié. Chaque fois que vous appelez Report, il délègue secrètement cela au contexte capturé. Dans l'exemple, le contexte capturé est l'interface utilisateur, ce qui signifie qu'aucune exception ne se produit.

25
Gusdor

Le Progress<T> le constructeur capture l'objet SynchronizationContext actuel.

La classe SynchronizationContext est une fonction qui résume les détails du modèle de thread impliqué. Autrement dit, dans Windows Forms, il utilisera Control.Invoke, dans WPF, il utilisera Dispatcher.Invoke, etc.

Quand le progress.Report est appelé, l'objet Progress lui-même sait qu'il doit exécuter son délégué en utilisant le SynchronizationContext capturé.

En d'autres termes, cela fonctionne parce que Progress a été conçu pour gérer cela sans que le développeur ait à le dire explicitement.

11
Jean Hominal

Il semble que vous soyez confus en raison du fait qu'une partie de cette machine cross-thread est cachée aux yeux des développeurs, vous n'avez donc qu'à "prendre et utiliser": http://blogs.msdn.com/b/dotnet /archive/2012/06/06/async-in-4-5-enabling-progress-and-cancellation-in-async-apis.aspx

Nous avons introduit l'interface IProgress pour vous permettre de créer une expérience d'affichage des progrès. Cette interface expose une méthode Report (T), que la tâche asynchrone appelle pour signaler la progression. Vous exposez cette interface dans la signature de la méthode async et l'appelant doit fournir un objet qui implémente cette interface. Ensemble, la tâche et l'appelant créent une liaison très utile (et peuvent s'exécuter sur différents threads).

Nous avons également fourni la classe Progress, qui est une implémentation d'IProgress. Vous êtes encouragé à utiliser Progress dans votre implémentation, car il gère toute la comptabilité relative à l'enregistrement et à la restauration du contexte de synchronisation. La progression expose à la fois un événement et un rappel d'action, qui sont appelés lorsque la tâche signale la progression. Ce modèle vous permet d'écrire du code qui réagit simplement aux changements de progression au fur et à mesure qu'ils se produisent. Ensemble, IProgress et Progress fournissent un moyen simple de transmettre des informations de progression d'une tâche d'arrière-plan au thread d'interface utilisateur.

Encore une chose à mentionner: la notification de progression sera invoquée après la fin du travail, pas seulement à ce moment . Donc, si votre thread d'interface utilisateur est inactif et que vous avez un cœur de processeur de rechange, le délai sera presque nul. Si votre thread d'interface utilisateur est occupé, la notification ne sera invoquée qu'au moment où le thread d'interface utilisateur sera de retour au repos (quel que soit le nombre de cœurs de CPU de rechange dont dispose votre ordinateur).

5
Yury Schkatula