2010-09-25 4 views
19

Je sais que si je modifie un contrôle d'un thread différent, je devrais faire attention car WinForms et WPF ne permettent pas de modifier l'état du contrôle d'autres threads.Pourquoi seul le thread d'interface utilisateur est-il autorisé à modifier l'interface utilisateur?

Pourquoi cette restriction est-elle en place?

Si je peux écrire du code thread-safe, je devrais pouvoir modifier l'état de contrôle en toute sécurité. Alors pourquoi cette restriction est-elle présente?

+0

Je crois que l'une des raisons pour lesquelles cette restriction existe est qu'il n'y a toujours pas plus et pas moins d'un thread garanti d'être à l'écoute de la boucle de message Windows. –

+1

@Tim: Ce n'est pas vrai, plusieurs threads d'interface utilisateur peuvent exister, chacun avec sa propre boucle de message. Par exemple, il est possible de lancer un deuxième thread pour afficher une boîte de dialogue de progression. –

+0

@Ben: cet article est étiqueté avec C#. Dans une application .Net, vous devez toujours ramener le contrôle à l'unité d'exécution de l'interface graphique pour effectuer un travail lié au dessin (ou risque de comportement erratique ou une exception). Bien sûr, cela ne vous empêche pas de générer un nombre illimité de threads pour effectuer un travail en arrière-plan. En ce qui concerne l'écoute des messages Win32 entrants réels, la seule façon que je sais de le faire (en dehors d'un appel externe) est avec "protected override void WndProc (ref Message m)". En théorie, pourrait prendre les messages transmis à cette méthode et les utiliser sur n'importe quel thread. –

Répondre

19

Plusieurs frameworks GUI ont cette limitation. Selon le livre Java Concurrency in Practice la raison en est d'éviter le verrouillage complexe. Le problème est que les contrôles de l'interface graphique peuvent avoir à réagir aux deux événements de l'interface utilisateur, la liaison de données et ainsi de suite, ce qui conduit à un verrouillage de plusieurs sources différentes et donc un risque d'interblocages. Pour éviter cela .NET WinForms (et autres interfaces utilisateur) restreint l'accès aux composants à un seul thread et évite ainsi le verrouillage.

+0

Ajoutez à cela l'interopérabilité avec les contrôles ActiveX (contrôle de Brower etc. - peut-être indirectement grâce à l'intégration de winforms) et vous avez également un héritage COM qui est Single Threaded Appartement. – TomTom

+1

Windows ** ne limite pas l'accès au thread propriétaire. Cette limitation est introduite par .NET. –

+0

@Ben: Je ne le savais pas. Je mettrai à jour ma réponse pour refléter cela. Je vais vérifier ce que le livre dit. –

3

.NET se réserve le droit d'accéder à votre contrôle dans le fil où vous l'avez créé à tout moment. Par conséquent, les accès provenant d'un autre thread ne peuvent jamais être thread safe.

8

Dans le cas de Windows, lorsqu'un contrôle est créé, les mises à jour de l'interface utilisateur sont effectuées via des messages provenant d'une pompe de message. Le programmateur n'a pas de contrôle direct du thread sur lequel la pompe fonctionne, donc l'arrivée d'un message pour un contrôle pourrait éventuellement entraîner le changement de l'état du contrôle. Si un autre thread (que le programmeur était en contrôle direct) était autorisé à changer l'état du contrôle, alors une sorte de logique de synchronisation devrait être mise en place pour empêcher la corruption de l'état du contrôle. Les contrôles dans .Net ne sont pas thread-safe; C'est, je suppose par conception. Mettre la logique de synchronisation dans tous les contrôles serait coûteux en termes de conception, de développement, de test et de support du code qui fournit cette fonctionnalité. Le programmeur peut bien sûr fournir la sécurité de thread au contrôle pour son propre code, mais pas pour le code qui est en .Net qui est en cours d'exécution en même temps que son code. Une solution à ce problème consiste à limiter ces types d'actions à un thread et à un thread uniquement, ce qui simplifie le maintien du code de contrôle dans .Net.

1

Vous pouvez créer votre propre code sans fil, mais vous n'avez aucun moyen d'injecter les primitives de synchronisation nécessaires dans le code WinForm et WPF intégré qui correspondent à ceux de votre code. Souvenez-vous qu'il y a beaucoup de messages qui circulent dans les coulisses et qui font que le thread de l'interface utilisateur accède au contrôle sans que vous en ayez vraiment conscience.

Un autre aspect intéressant d'une affinité de thread de contrôle est qu'il pourrait (bien que je soupçonne qu'ils ne le feraient jamais) utiliser le modèle Thread Local Storage. Évidemment, si vous accédiez à un contrôle sur un thread autre que celui sur lequel il était créé, il ne serait pas capable d'accéder aux données TLS correctes, peu importe avec quelle précision vous avez structuré le code pour vous protéger de tous les problèmes normaux du code multithread.

1

En fait, autant que je sache, c'était le plan depuis le début! Chaque contrôle pourrait être accessible à partir de n'importe quel fil! Et juste parce que le verrouillage des threads était nécessaire lorsqu'un autre thread nécessitait l'accès au contrôle - et parce que le verrouillage est coûteux - un nouveau modèle de thread a été conçu appelé «thread location». Dans ce modèle, les contrôles associés seraient agrégés en "contextes" en utilisant un seul thread, réduisant ainsi la quantité de verrouillage nécessaire. Assez cool, hein? Malheureusement, cette tentative était trop audacieuse pour réussir (et un peu plus complexe parce que le verrouillage était toujours nécessaire), donc le bon vieux modèle de thread Windows Forms --avec le thread UI unique et avec le thread de création pour revendiquer la propriété de le control-- est utilisé à nouveau dans wPF pour rendre nos vies ... plus faciles?

1

Windows prend en charge de nombreuses opérations qui, en particulier lorsqu'elles sont combinées, ne sont intrinsèquement pas sécurisées pour les threads. Que devrait-il se passer, par exemple, si un thread essaye d'insérer du texte dans un champ de texte en commençant par le 50ème caractère, alors qu'un autre thread essaie de supprimer les 40 premiers caractères de ce champ? Windows pourrait utiliser des verrous pour s'assurer que la seconde opération ne pourrait pas commencer avant la fin du premier, mais l'utilisation de verrous ajouterait une surcharge à chaque opération et augmenterait la possibilité d'interblocage si les actions sur une entité nécessitent manipulation d'un autre. Exiger que les actions impliquant une fenêtre particulière doivent se produire sur un thread particulier est une exigence plus stricte que ce qui serait nécessaire pour empêcher que des combinaisons d'opérations dangereuses soient effectuées simultanément, mais il est relativement facile à analyser. Utiliser des contrôles provenant de plusieurs threads et éviter les conflits via d'autres moyens serait généralement plus difficile.

Questions connexes