2010-05-04 2 views

Répondre

13

(Simplification avance)

Un thread d'interface utilisateur est un fil simple Appartement de filetage qui est utilisé pour créer divers objets de l'interface utilisateur (en Winforms, cela signifie que les témoins). Par convention et règle, un contrôle peut seulement être accédé à partir du thread qui a été utilisé pour le créer; faire autrement peut et produira des résultats inattendus, à partir de bizarreries visuelles jusqu'à un accident.

Sauf si vous en créez explicitement d'autres, il n'existe qu'un seul thread d'interface utilisateur dans une application Windows Forms. Bien que vous puissiez créer un autre thread et lancer une boucle de message, il y a très peu de raisons pour lesquelles vous voudriez le faire, et deux threads d'interface utilisateur différents ne peuvent pas "se parler" plus que n'importe quel autre thread peut parler à un Fil d'interface utilisateur.

+2

+1 pour mentionner que vous pouvez le faire, mais que c'est rarement comme ça que les choses devraient être faites ... –

+0

Alors qu'en est-il de quand vous faites du nouveau Thread(). Start() dans un STAThread? –

+0

@Griever: Cela démarre un nouveau thread, mais il est complètement séparé du thread de l'interface utilisateur. Faire un nouveau Thread start n'importe où crée un nouveau thread indépendant. –

0

ÉDITÉ pour l'exactitude:

Il y a un thread d'interface utilisateur par application active dans Windows Forms et concept similaire pour WPF.

Par exemple: Lorsque vous démarrez l'application, il y a un thread, il devient un thread d'interface utilisateur lorsque Application.Run (new Form1()); est appelé.

Si vous essayez de faire Application.Run (new Form2()); à l'exécution, vous obtiendrez "System.InvalidOperationException: Démarrer une deuxième boucle de messages sur un seul thread n'est pas une opération valide. Utilisez plutôt Form.ShowDialog."

Si vous aviez vraiment besoin de deux formulaires séparés pour ne pas partager le même fil, vous devez créer un nouveau fil, puis appeler Application.Run (new MyForm()) etc. Ceci n'est pas commun.

+1

Ceci n'est pas vrai. Différentes formes, dans Windows Forms, utilisent toutes le ** même ** thread. La pompe à message les rend tous sensibles. –

+0

Je l'avais inversé, les boîtes de dialogue modales nécessitent un nouveau fil, les boîtes de dialogue non modales ne requièrent pas * un nouveau fil. Le point reste que les threads normaux peuvent devenir des threads ui quand un nouveau formulaire est lancé à partir d'eux. – David

+0

Ceci est encore faux. "Lancement d'un formulaire" à partir d'un nouveau thread, sans un soin particulier pour lancer une boucle de message, provoquera toutes sortes de problèmes, y compris (dans la plupart des cas) un plantage dû au manque de STA par défaut. –

7

Un fil interface utilisateur a un certain nombre de caractéristiques qui le rendent spécial:

  • Windows a une file d'attente de message associé au fil. Cela arrive dès que la toute première fenêtre est créée sur le thread.
  • Le thread exécute une boucle de message, permettant à Windows d'envoyer des messages aux fenêtres. La boucle de message est créée dès que vous appelez Application.Run().
  • COM a été initialisé sur le thread, demandant un appartement monothread. Un STA est nécessaire pour permettre à de nombreuses fonctionnalités Windows de fonctionner correctement, des fonctionnalités qui ne sont pas sécurisées par les threads. COM s'assure que ces fonctionnalités sont toujours appelées de manière thread-safe, marshaling l'appel d'un thread de travail au thread STA en fonction des besoins. Les exemples de ces fonctionnalités sont Drag + Drop, le presse-papiers, les dialogues shell (OpenFileDialog etc), les contrôles ActiveX comme WebBrowser, les hooks de fenêtre définis par SetWindowsHookEx, les fournisseurs d'accessibilité tels que ceux utilisés par un lecteur d'écran. Tout le code externe, aucun thread-safe.
  • Le thread ne bloque jamais sur aucune opération, il reste réactif afin qu'il puisse envoyer des messages Windows selon les besoins pour que l'interface utilisateur reste réactive et que les demandes de marshaling COM circulent. Faire des appels à WaitHandle.WaitAny() par exemple est explicitement interdit et génère une exception. Le CLR a un support spécifique pour WaitOne() et lock, pompant une boucle de message interne pour éviter les interblocages.

Le thread de démarrage d'un processus est presque toujours sélectionné comme thread d'interface utilisateur, bien que ce ne soit pas une exigence stricte. L'état STA est sélectionné par l'attribut [STAThread] de la méthode Main().

Vous pouvez créer un thread d'interface utilisateur en vérifiant que les conditions ci-dessus sont remplies. Cela pourrait ressembler à ceci dans une application Winforms:

var ui = new Thread(() => { Application.Run(new Form2()); }); 
    ui.SetApartmentState(ApartmentState.STA); 
    ui.Start(); 

Cela crée une deuxième fenêtre, en cours d'exécution sur son propre thread d'interface utilisateur. Un problème typique que vous avez avec cet arrangement est que vous avez maintenant deux fenêtres séparées, elles ne sont pas associées les unes aux autres. La 2ème fenêtre ne peut pas être possédée par le 1er, elle a son propre Z-ordre indépendant du 1er. Difficile à traiter par l'utilisateur. L'événement SystemEvents.UserPreferenceChanged est notable, il déclenchera inévitablement son événement sur le mauvais thread et cela est susceptible de provoquer un blocage. Beaucoup de contrôles WinForms l'abonnent. Sauf dans de rares circonstances, comme un écran de démarrage, cela n'améliore pas du tout l'interface utilisateur.

+0

J'aime votre réponse détaillée. –

Questions connexes