Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Fathi Bellahcene

.Net m'a tuer!
[framework 4] Les Tasks et le Thread UI

 

Je viens de passer quelques temps au TechDay’s et j’ai pu voir pas mal de session intéressante. Par contre une chose m’a un peu étonné lors de certaines de ces sessions qui abordaient les améliorations du framework .NET (donc le 4.5) : en gros, beaucoup de speaker expliquaient qu’avec l’API Task, il est très compliqué de revenir vers le thread UI sauf à passer par les méthodes old school (BeginInvoke,EndInvoke)…du coup, comme on se tape toute la tuyauterie, plus besoin de Task Sourire.

Pour eux, la solution passe par l’utilisation de async/await ce qui va nous permettre de régler ce problème de manière élégante MAIS mon problème ce que pour moi, il existe des moyen technique assez simple (aussi simple que async/await) pour régler ce problème, en particulier avec PRISM.

Du coup, je me suis dit que ca valait peut être le coup de faire un petit post là dessus.

Je vais donc vous montrer ici comment je fais ca simplement avec la classe Eventaggregator (disponible avec PRISM)

Prenons l’exemple suivant:un bouton qui lorsque l’on click dessus renseigne le contenu d’une textBox avec un petit sleep pour simuler une activitée qui prend du temps:

 

   1: private void button1_Click(object sender, RoutedEventArgs e)
   2:       {
   3:         
   4:           System.Threading.Thread.Sleep(5000);
   5:           textBox1.Text = "toto";
   6:         
   7:       }

si j’exécute mon appli, mon interface est gelée pendant 5 secondes…ce qui est inacceptable pour un utilisateur. Du coup, on souhaite faire ca en parallèle. Mon code devient donc:

   1: private void button1_Click(object sender, RoutedEventArgs e)
   2:    {
   3:        var t = Task.Factory.StartNew(() =>
   4:         {
   5:             System.Threading.Thread.Sleep(5000);
   6:             textBox1.Text = "toto";
   7:         });
   8:  
   9:    }

…mais a l’exécution j’ai la fameuse erreur du Thread UI:

image

et pour régler ca, j’utilise l’EventAggregator:

   1: public partial class MainWindow : Window
   2:   {
   3:       private IEventAggregator eventAggregator = new EventAggregator();
   4:       public MainWindow()
   5:       {
   6:           InitializeComponent();
   7:           eventAggregator.GetEvent<CompositePresentationEvent<string>>().
   8:               Subscribe(val => textBox1.Text = val
   9:                           , ThreadOption.UIThread);
  10:       }
  11:  
  12:       private void button1_Click(object sender, RoutedEventArgs e)
  13:       {
  14:           var t = Task.Factory.StartNew<string>(() =>
  15:           {
  16:               System.Threading.Thread.Sleep(5000);
  17:               return "toto";
  18:           }).ContinueWith((task) => 
  19:               eventAggregator.GetEvent<CompositePresentationEvent<string>>()
  20:               .Publish(task.Result));
  21:       }
  22:  
  23:  
  24:   }
 
Il y a trois choses importantes dans ce code:
  • On passe par un évènement pour synchroniser notre interface graphique avec notre tâche. Cet évènement est complètement géré par PRISM via la classe EventAggregator (qui est une pur merveille). Ici, j’ai utilisé l’objet de base (CompositePresentationEvent<string>) mais dans la pratique on va pouvoir avoir des objets plus complexes et mieux adaptés: si notre traitement renvoi un objet de type Client pour mettre à jours un ensemble de contrôles, on va plutôt utiliser la classe CompositePresentationEvent<Client>.
  • L’utilisation de l’EventAggregator pour repasser dans le thread UI: la méthode Subscirbe qui permet de s’abonner à notre évènement à comme paramètre ThreadOption.UIThread, sans cette option, rien ne marche.
  • A la fin de ma tâche, on lance une action qui à uniquement pour but de lever un évènement avec le résultat du traitement. Comme on est dans le ContinueWith, on est sûr que le traitement est fini et OK.
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :
Posted: mercredi 8 février 2012 23:33 par fathi
Classé sous : , , ,

Commentaires

richardc a dit :

heu... y'a pas une option dans ContinueWith qui permet de retourner dans le thread UI ?

# février 9, 2012 14:19

fathi a dit :

oui tu as raison et je suis bête de ne pas avoir commencé par ca.

tu peut récupérer avec la classe TaskScheduler un "contexte" que tu peu spécifier dans le ContinueWith.

et dans mon exemple ca irait très bien.

Le seul hic avec cette méthode (la raison pour laquelle je ne l'utilise pas) c'est que tu doit initialiser ton contexte depuis une méthode qui s’exécute dans le thread UI...un peu chiant quand tu te "promène" dans du code un peu complexe.

l'utilisation de l'EventAggregator te permet, entre autre, de ne pas avoir a te poser cette question.

# février 9, 2012 20:25
Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- VMMap en mode instrumentation sur système 64bit : attention à la plateforme cible du build .NET par CoqBlog le il y a 6 heures et 44 minutes

- Etendre le Team Web Access de TFS 2012 – Step 0 par Philippe Didiergeorges Aka Philess le 05-23-2013, 23:48

- Simuler facilement l’envoi de mail par Blog de Jérémy Jeanson le 05-22-2013, 12:52

- ProcDump 6.0 : support du filtrage sur messages d'exceptions .NET, des filtres multiples et du ciblage par nom de service par CoqBlog le 05-20-2013, 14:50

- Votez pour le TOP 10 des influenceurs SharePoint francophones ! par Le blog de Patrick [MVP SharePoint] le 05-20-2013, 12:59

- [Conf’SharePoint] Dernier rappel ! :-) par Le blog de Patrick [MVP SharePoint] le 05-20-2013, 09:09

- [ #SharePoint 2013 ] les modèles de sites standards… par Le blog de Patrick [MVP SharePoint] le 05-20-2013, 09:03

- 10 erreurs de compréhension concernant SharePoint… par Le blog de Patrick [MVP SharePoint] le 05-20-2013, 08:27

- Conf’SharePoint : 10 bonnes raisons pour ne pas la rater par Le petit blog de Pierre / Pierre's little blog le 05-14-2013, 02:24

- [Event] Soirée de lancement Agile .NET France à Lyon par Blog Agile/ALM de Vincent THAVONEKHAM le 05-13-2013, 01:29