ApartmentState pour les nuls


120

Je viens de corriger un bug en utilisant ceci:

_Thread.SetApartmentState(ApartmentState.STA);

Maintenant, j'aimerais comprendre ce que cela signifie et pourquoi cela fonctionne!


1
Ce message peut vous aider
Arsen Mkrtchyan

Réponses:


236

COM est le grand-père de .NET. Ils avaient des objectifs assez ambitieux, l'une des choses que COM fait mais que .NET ignore complètement est de fournir des garanties de thread pour une classe. Une classe COM peut publier le type d'exigences de thread dont elle dispose. Et l'infrastructure COM garantit que ces exigences sont satisfaites.

Ceci est complètement absent dans .NET. Vous pouvez utiliser un objet Queue <> par exemple dans plusieurs threads, mais si vous ne verrouillez pas correctement, vous aurez un bogue désagréable dans votre code qui est très difficile à diagnostiquer.

Les détails exacts du filetage COM sont trop volumineux pour tenir dans un article. Je vais me concentrer sur les détails de votre question. Un thread qui crée des objets COM doit indiquer à COM quel type de support il souhaite donner aux classes COM qui ont des options de thread restreintes. La grande majorité de ces classes ne prennent en charge que ce qu'on appelle le thread Apartment, leurs méthodes d'interface ne peuvent être appelées en toute sécurité qu'à partir du même thread qui a créé l'instance. En d'autres termes, ils annoncent "Je ne supporte pas du tout le threading, veillez à ne jamais m'appeler du mauvais thread". Même si le code client en fait ne l' appeler d'un autre fil.

Il existe deux types, STA (Single Threaded Apartment) et MTA. Il est spécifié dans l'appel CoInitializeEx (), une fonction qui doit être appelée par tout thread qui fait quoi que ce soit avec COM. Le CLR effectue cet appel automatiquement chaque fois qu'il démarre un thread. Pour le thread de démarrage principal de votre programme, il obtient la valeur à passer de l'attribut [STAThread] ou [MTAThread] sur votre méthode Main (). La valeur par défaut est MTA. Pour les threads que vous créez vous-même, il est déterminé par votre appel à SetApartmentState (). La valeur par défaut est MTA. Les threads de Threadpool sont toujours MTA, qui ne peuvent pas être modifiés.

Il y a beaucoup de code dans Windows qui nécessite un STA. Les exemples notables que vous utiliseriez vous-même sont le Presse-papiers, le glisser-déposer et les boîtes de dialogue shell (comme OpenFileDialog). Et beaucoup de code que vous ne pouvez pas voir, comme les programmes UI Automation et les hooks pour observer les messages. Aucun de ce code ne doit être thread-safe, son auteur aurait beaucoup de mal à le sécuriser sans savoir dans quel programme il est utilisé. En conséquence, le thread d'interface utilisateur d'un projet WPF ou Windows Forms doit toujours être STA pour prendre en charge ce code, comme le fait tout thread qui crée une fenêtre.

La promesse que vous faites à COM que votre fil est STA mais ne vous oblige à suivre le contrat appartement single-thread. Ils sont assez raides et vous pouvez être difficile à diagnostiquer des problèmes lorsque vous rompez le contrat. Les conditions requises sont que vous ne bloquiez jamais le thread pendant un certain temps et que vous pompiez une boucle de message. Cette dernière exigence est remplie par un thread d'interface utilisateur WPF ou Winforms, mais vous devrez vous en occuper vous-même si vous créez votre propre thread STA. Le diagnostic commun pour rompre le contrat est l'impasse.

Il y a un peu de support intégré dans le CLR pour prendre en charge ces exigences, vous aidant à éviter les ennuis. L' instruction lock et les méthodes WaitOne () pompent une boucle de message lorsqu'elle se bloque sur un thread STA. Cependant, cela ne prend en compte que l'exigence de ne jamais bloquer, vous devez toujours créer votre propre boucle de message. Application.Run () dans WPF et Winforms.

J'ai déjà contribué une réponse qui contient plus de détails sur l'importance d'avoir une boucle de message pour garder COM heureux. Vous trouverez le message ici .


4
Excellente réponse! Le bogue que j'ai résolu concernait un thread que j'ai créé pour un générateur de rapports de longue durée qui utilisait des contrôles WPF pour créer des parties du rapport, ce qui a du sens, même si je ne suis pas conscient que ce thread a une boucle de message dessus .
Benjol

4
J'ai sérieusement dû lire le message MSDN plusieurs fois pour le comprendre, votre réponse est très claire et bien écrite. Je vous remercie!
ak3nat0n
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.