2009-09-02 6 views
3

Mon problème est le suivant:Est-il possible de surcharger un thread en utilisant ISynchronizeInvoke.BeginInvoke()?

J'ai deux threads, mon thread d'interface utilisateur et un thread de travail. Mon thread de travail s'exécute dans une classe séparée qui est instanciée par le formulaire, qui se passe comme ISynchronizeInvoke à la classe worker, qui utilise ensuite Invoke sur cette interface pour appeler ses événements, qui fournissent des mises à jour d'état à l'interface utilisateur pour l'affichage. Cela fonctionne à merveille. J'ai remarqué que mon fil d'arrière-plan semblait fonctionner lentement, donc j'ai changé l'appel à Invoke en BeginInvoke, pensant que "je ne fais que fournir des mises à jour de progrès, il n'a pas besoin d'être exactement synchrone, pas de mal fait "sauf que maintenant je reçois des bizarreries avec la mise à jour de progrès. Ma barre de progression se met à jour, mais pas le texte de l'étiquette, et si je change de fenêtre pour essayer de revenir en arrière, elle se comporte comme si le fil de l'interface utilisateur était verrouillé. souvent) surchargent tellement le thread d'UI qu'il ne traite jamais les messages. Est-ce possible ou y a-t-il quelque chose d'autre ici?

Répondre

3

Vous surchargez définitivement le fil d'interface utilisateur.

Dans votre premier exemple, vous étiez (dans les coulisses) en train d'envoyer un message au thread d'interface utilisateur, en attente de traitement (c'est le but de invoke, qui repose finalement sur SendMessage), puis en en envoyant un autre. Entre-temps, d'autres messages ont probablement été mis en file d'attente (messages WM_PAINT, par exemple) et traités. Dans votre deuxième exemple, en utilisant BeginInvoke (qui repose finalement sur PostMessage), vous avez massivement mis en file d'attente un grand nombre de messages dans la file d'attente de messages, que la pompe de message doit gérer séquentiellement. Et bien sûr, pendant qu'il traite ces milliers de messages, il ne peut pas gérer les messages du système d'exploitation (WM_PAINT, etc ..) qui rendent votre interface utilisateur "figée"

Vous fournissez probablement trop de mises à jour de statut; essayez d'abaisser le niveau de retour.

Si vous voulez mieux comprendre comment les messages fonctionnent dans Windows, this est le point de départ.

1

Quelques réflexions;

  • essayez de mettre à jour vos mises à jour; par exemple, il est inutile de mettre à jour pour chaque itération dans une boucle; en fonction de la vitesse, peut-être tous les 50/500. Dans le cas de listes, vous tampon dans une variable de liste locale, prendre la liste via Invoke/BeginInvoke, et traiter le tampon sur le fil de l'interface utilisateur
  • capture de variable; Si vous utilisez BeginInvoke et des méthodes anonymes, vous pourriez avoir des problèmes ... Je vais ajouter un exemple ci-dessous
  • rendant la mise à jour de l'interface utilisateur efficace - en particulier si vous traitez une liste; certains contrôles (en particulier les contrôles basés sur des listes) ont une paire de méthodes comme BeginEdit/EndEdit, qui arrêtent le redessin de l'interface utilisateur lorsque vous faites beaucoup de mises à jour; à la place, il attend que le End* est appelé

problème de capture ... imaginez (travailleur):

List<string> stuff = new List<string>(); 
for(int i = 0 ; i < 50000 ; i++) { 
    stuff.Add(i.ToString()); 
    if((i % 100) == 0) { 
     // update UI 
     BeginInvoke((MethodInvoker) delegate { 
      foreach(string s in stuff) { 
       listBox.Items.Add(s); 
      } 
     }); 
    } 
} 

Avez-vous remarqué que, à un moment donné les deux fils parlent à stuff?Le thread de l'interface utilisateur peut l'itérer, tandis que le thread de travail (qui a continué à tourner au-delà de BeginInvoke) continue d'ajouter. Cela peut causer des problèmes. Généralement performance problèmes (sauf si vous attrapez les exceptions et prendre beaucoup de temps pour les enregistrer), mais certainement des problèmes. Options seraient ici les suivantes:

  • utilisant Invoke pour lancer la mise à jour de façon synchrone
  • créer un nouveau tampon par mise à jour, de sorte que les deux fils n'ont jamais la même instance de liste (vous auriez besoin de regarder très attentivement la variable à portée de vue, cependant)
Questions connexes