Dans WinForms, pourquoi ne pouvez-vous pas mettre à jour les contrôles de l'interface utilisateur à partir d'autres threads ?

StackOverflow https://stackoverflow.com/questions/10349

  •  08-06-2019
  •  | 
  •  

Question

Je suis sûr qu'il y a une bonne (ou du moins décente) raison à cela.Qu'est-ce que c'est?

Était-ce utile?

La solution

Parce que vous pouvez facilement vous retrouver dans une impasse (entre autres problèmes).

Par exemple, votre thread secondaire pourrait essayer de mettre à jour le contrôle de l'interface utilisateur, mais le contrôle de l'interface utilisateur attendra qu'une ressource verrouillée par le thread secondaire soit libérée, de sorte que les deux threads finissent par s'attendre l'un l'autre pour terminer.Comme d'autres l'ont fait remarquer, cette situation n'est pas propre au code de l'interface utilisateur, mais elle est particulièrement courante.

Dans d'autres langages tels que C++, vous êtes libre d'essayer de le faire (sans qu'une exception ne soit levée comme dans WinForms), mais votre application peut se bloquer et cesser de répondre en cas de blocage.

Par ailleurs, vous pouvez facilement indiquer au thread d'interface utilisateur que vous souhaitez mettre à jour un contrôle, créez simplement un délégué, puis appelez la méthode (asynchrone) BeginInvoke sur ce contrôle en lui passant votre délégué.Par exemple.

myControl.BeginInvoke(myControl.UpdateFunction);

C'est l'équivalent de faire un PostMessage C++/MFC à partir d'un thread de travail

Autres conseils

Je pense que c'est une question brillante - et je pense qu'il y a besoin d'une meilleure réponse.

La seule raison est sûrement qu'il y a quelque chose dans un cadre quelque part qui n'est pas très filial.

Ce "quelque chose" correspond à presque tous les membres d'instance sur chaque contrôle de System.Windows.Forms.

La documentation MSDN pour de nombreux contrôles dans System.Windows.Forms, sinon tous, par exemple "Tous les membres publics statiques (partagés dans Visual Basic) de ce type sont thread-safe.Il n'est pas garanti que les membres de l'instance soient thread-safe."

Cela signifie que les membres de l'instance tels que TextBox.Text {get; set;} ne sont pas réentrant.

Rendre chacun de ces membres d'instance thread-safe pourrait introduire beaucoup de surcharge dont la plupart des applications n'ont pas besoin.Au lieu de cela, les concepteurs du framework .Net ont décidé, et je pense à juste titre, que la charge de la synchronisation de l'accès aux contrôles de formulaires à partir de plusieurs threads devait incomber au programmeur.

[Modifier]

Bien que cette question demande uniquement « pourquoi », voici un lien vers un article qui explique « comment » :

Comment:Effectuer des appels Thread-Safe aux contrôles Windows Forms sur MSDN

http://msdn.microsoft.com/en-us/library/ms171728.aspx

Bien que cela semble raisonnable, la réponse de John n'est pas correcte.En fait, même lorsque vous utilisez Invoke, vous n'êtes toujours pas en sécurité pour ne pas vous retrouver dans des situations de blocage.Lorsque vous traitez des événements déclenchés sur un thread en arrière-plan à l'aide d'Invoke, cela peut même conduire à ce problème.


La vraie raison a davantage à voir avec les conditions de course et remonte à l’époque ancienne de Win32.Je ne peux pas expliquer les détails ici, les mots-clés sont les pompes de messages, les événements WM_PAINT et les différences subtiles entre "SEND" et "POST".


De plus amples informations peuvent être trouvées ici ici et ici.

Dans la version 1.0/1.1, aucune exception n'a été levée lors du débogage, ce que vous avez obtenu à la place était un scénario de blocage intermittent de l'exécution.Bon!:) Par conséquent, avec 2.0, ils ont fait lancer ce scénario une exception et à juste titre.

La véritable raison en est probablement (comme le dit Adam Haile) une sorte de problème de concurrence/verrouillage.Notez que l'API .NET normale (telle que TextBox.Text = "Hello";) encapsule les commandes SEND (qui nécessitent une action immédiate) qui peuvent créer des problèmes si elles sont effectuées sur un thread distinct de celui qui effectue la mise à jour.L'utilisation d'Invoke/BeginInvoke utilise à la place un POST qui met l'action en file d'attente.

Plus d’informations sur ENVOYER et POST ici.

C'est pour que vous n'ayez pas deux choses à essayer de mettre à jour le contrôle en même temps.(Cela pourrait se produire si le CPU passe à l'autre thread au milieu d'une écriture / lecture) Même raison pour laquelle vous devez utiliser des mutexes (ou une autre synchronisation) lorsque vous accédez à des variables partagées entre plusieurs threads.

Modifier:

Dans d'autres langues telles que C ++, vous êtes libre d'essayer de le faire (sans exception jetée comme dans WinForms), mais vous finirez par apprendre à la dure!

Ahh oui...Je bascule entre C/C++ et C# et c'était donc un peu plus générique que j'aurais dû l'être, désolé...Il a raison, toi peut faites cela en C/C++, mais cela reviendra vous mordre !

Il serait également nécessaire d'implémenter la synchronisation au sein des fonctions de mise à jour sensibles à l'appel simultané.Faire cela pour les éléments de l'interface utilisateur serait coûteux au niveau de l'application et du système d'exploitation, et complètement redondant pour la grande majorité du code.

Certaines API permettent de modifier la propriété actuelle des threads d'un système afin que vous puissiez mettre à jour temporairement (ou définitivement) les systèmes à partir d'autres threads sans avoir besoin de recourir à la communication entre les threads.

Hmm, je ne suis pas sûr, mais je pense que lorsque nous avons des contrôles de progression comme des barres d'attente, des barres de progression, nous pouvons mettre à jour leurs valeurs à partir d'un autre thread et tout fonctionne très bien sans aucun problème.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top