La réponse à cette question réside dans le fonctionnement des contrôles C #
Les contrôles dans Windows Forms sont liés à un thread spécifique et ne sont pas thread-safe. Par conséquent, si vous appelez la méthode d'un contrôle à partir d'un thread différent, vous devez utiliser l'une des méthodes d'appel du contrôle pour marshaler l'appel vers le thread approprié. Cette propriété peut être utilisée pour déterminer si vous devez appeler une méthode invoke, ce qui peut être utile si vous ne savez pas quel thread possède un contrôle.
De Control.InvokeRequired
En fait, ce que fait Invoke est de s'assurer que le code que vous appelez se produit sur le thread sur lequel le contrôle «vit», empêchant efficacement les exceptions entre threads.
D'un point de vue historique, dans .Net 1.1, cela était en fait autorisé. Cela signifiait que vous pouviez essayer d'exécuter du code sur le thread "GUI" à partir de n'importe quel thread d'arrière-plan et cela fonctionnerait principalement. Parfois, cela provoquait simplement la fermeture de votre application parce que vous interrompiez effectivement le thread GUI pendant qu'il faisait autre chose. Il s'agit de l' exception de threads croisés - imaginez que vous essayez de mettre à jour une zone de texte pendant que l'interface graphique peint autre chose.
- Quelle action est prioritaire?
- Est-il même possible que les deux se produisent en même temps?
- Qu'arrive-t-il à toutes les autres commandes que l'interface graphique doit exécuter?
En effet, vous interrompez une file d'attente, ce qui peut avoir de nombreuses conséquences imprévues. Invoke est en fait le moyen «poli» d'obtenir ce que vous voulez faire dans cette file d'attente, et cette règle a été appliquée à partir de .Net 2.0 via une exception InvalidOperationException. .
Pour comprendre ce qui se passe réellement dans les coulisses, et ce que l'on entend par "fil GUI", il est utile de comprendre ce qu'est une pompe de message ou une boucle de message.
Ceci est en fait déjà répondu à la question " Qu'est-ce qu'une pompe à messages " et il est recommandé de le lire pour comprendre le mécanisme réel auquel vous vous connectez lorsque vous interagissez avec les contrôles.
D'autres lectures que vous pourriez trouver utiles incluent:
Quoi de neuf avec Begin Invoke
L'une des règles cardinales de la programmation de l'interface graphique Windows est que seul le thread qui a créé un contrôle peut accéder et / ou modifier son contenu (à l'exception de quelques exceptions documentées). Essayez de le faire à partir de n'importe quel autre thread et vous obtiendrez un comportement imprévisible allant du blocage aux exceptions en passant par une interface utilisateur à moitié mise à jour. La bonne façon de mettre à jour un contrôle à partir d'un autre thread est alors de publier un message approprié dans la file d'attente de messages de l'application. Lorsque la pompe de message arrive à exécuter ce message, le contrôle sera mis à jour, sur le même thread qui l'a créé (rappelez-vous, la pompe de message s'exécute sur le thread principal).
et, pour un aperçu plus lourd du code avec un échantillon représentatif:
Opérations cross-thread non valides
// the canonical form (C# consumer)
public delegate void ControlStringConsumer(Control control, string text); // defines a delegate type
public void SetText(Control control, string text) {
if (control.InvokeRequired) {
control.Invoke(new ControlStringConsumer(SetText), new object[]{control, text}); // invoking itself
} else {
control.Text=text; // the "functional part", executing only on the main thread
}
}
Une fois que vous aurez compris InvokeRequired, vous souhaiterez peut-être envisager d'utiliser une méthode d'extension pour encapsuler ces appels. Ceci est bien couvert dans la question de débordement de pile Nettoyage du code jonché avec Invoke requis .
Il y a aussi un autre article sur ce qui s'est passé historiquement qui peut être intéressant.