Pourquoi les contrôles d’interface utilisateur dans WPF ont-ils une affinité de thread?

Pourquoi est-ce que le thread qui a créé le contrôle est celui qui peut le mettre à jour? Pourquoi MS n’a-t-il pas donné aux utilisateurs la possibilité d’utiliser le locking et d’autres techniques de synchronisation de threads pour lire et écrire dans les propriétés des contrôles de l’interface utilisateur comportant plusieurs threads

La description courte par MSDN est

Le modèle de thread de WPF a été synchronisé avec le modèle de thread User32 existant, qui consiste en une exécution en un seul thread avec affinité de thread. L’interopérabilité en était la raison principale: des systèmes tels que OLE 2.0, le Presse-papiers et Internet Explorer requièrent tous une exécution STA (Single Thread Affinity).

La description la plus longue est la suivante:

La plupart des objects de WPF dérivent de DispatcherObject, qui fournit les constructions de base permettant de gérer la concurrence et les threads. WPF est basé sur un système de messagerie implémenté par le répartiteur. Cela fonctionne beaucoup comme la pompe de message Win32 familière; En fait, le répartiteur WPF utilise les messages User32 pour effectuer des appels multithreads.

Lors de la discussion sur l’access simultané dans WPF, il faut comprendre deux concepts fondamentaux: l’affinité du répartiteur et celle du thread.

Au cours de la phase de conception de WPF, l’objective était de passer à un seul thread d’exécution, mais à un modèle “affinitisé” sans thread. Une affinité de thread se produit lorsqu’un composant utilise l’identité du thread en cours d’exécution pour stocker un type d’état. La forme la plus courante consiste à utiliser le magasin local de thread (TLS) pour stocker l’état. L’affinité de thread requirejs que chaque thread logique d’exécution appartienne à un seul thread physique du système d’exploitation, ce qui peut nécessiter une mémoire importante. En fin de compte, le modèle de threading de WPF a été maintenu en phase avec le modèle de threading User32 existant d’exécution à un seul thread avec affinité de thread. L’interopérabilité en était la principale raison. Des systèmes tels que OLE 2.0, le Presse-papiers et Internet Explorer requièrent tous une exécution STA (Single Thread Affinity).

Étant donné que vous avez des objects avec un thread STA, vous avez besoin d’un moyen de communiquer entre les threads et de valider que vous êtes sur le bon thread. C’est là que réside le rôle du répartiteur. Le répartiteur est un système de dissortingbution de messages de base, avec plusieurs files d’attente hiérarchisées. Des exemples de messages incluent les notifications d’entrée brutes (souris déplacée), les fonctions-cadre (mise en page) ou les commandes utilisateur (exécuter cette méthode). En dérivant de DispatcherObject, vous créez un object CLR ayant un comportement STA et un pointeur sur un répartiteur au moment de la création.

Vous pouvez lire l’article complet ici

Personnellement, je préfère le modèle à un seul thread de WPF plutôt que de devoir utiliser des techniques de locking et de synchronisation des threads. Dispatcher peut être utilisé pour transmettre des messages au thread principal de l’interface utilisateur à différents niveaux de priorité , ce qui prend en charge la plupart des processus d’arrière-plan de petite taille. Si vous avez besoin d’un traitement lourd, vous pouvez toujours créer votre propre thread d’arrière-plan pour cela.

WPF, comme pratiquement tous les kits d’interface utilisateur, fonctionne en pompant une boucle de messages. Comme les messages peuvent arriver à tout moment et affecter n’importe quel contrôle, vous auriez besoin d’un verrou global. Et pour le rendre moins sujet aux erreurs, vous voulez probablement une fonction qui invoquerait un délégué sous le verrou. Peut-être quelque chose comme ça:

Dispatcher.Invoke(Delegate, Object()) 

Le fait que cela soit organisé dans le thread d’interface utilisateur au lieu d’acquérir un verrou global n’est qu’un détail d’implémentation.