Invoke(Délégué)
est-ce que quelqu'un peut expliquer cette Déclaration écrite sur ce lien
Invoke(Delegate):
exécute le délégué spécifié sur le thread qui possède la poignée de fenêtre sous-jacente de la commande .
est-ce que quelqu'un peut expliquer ce que cela signifie (surtout l'audacieux) Je ne suis pas en mesure de l'obtenir clairement
9 réponses
la réponse à cette question réside dans la façon dont C# Controls fonctionne
Les commandesdans les formes Windows sont liées à un thread spécifique et ne sont pas thread-safe. Par conséquent, si vous appelez une méthode de contrôle d'un différent thread, vous devez utiliser l'une des méthodes invoke du contrôle pour marshal l'appel au bon fil. 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 fil propriétaire d'un contrôle.
À Partir De .InvokeRequired
effectivement, ce Qu'invoque fait 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 filetées croisées.
d'un point de vue historique, dans .Net 1.1, cela a été autorisé. Cela signifie que vous pouvez essayer et exécuter du code sur le thread" GUI " à partir de n'importe quel arrière-plan. fil et cela fonctionnerait surtout. Parfois, il serait juste cause de votre application à la sortie parce que vous étiez effectivement interupting le fil GUI tandis qu'il faisait quelque chose d'autre. Il s'agit de la Cross Threaded Exception - imaginez essayer de mettre à jour une boîte de texte alors que l'interface graphique est en train de peindre autre chose.
- quelle action 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?
effectivement, vous êtes en train d'intercaler une file d'attente, qui peut avoir beaucoup de conséquences imprévues. Invoke est effectivement la façon " polie "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 un jet InvalidOperationException .
pour comprendre ce qui se passe réellement dans les coulisses, et ce qui est par "GUI Thread", il est utile de comprendre ce qu'est une pompe de Message ou une boucle de Message.
c'est en fait déjà répondu dans la question " Qu'est-ce qu'une pompe à Message " et est recommandé la lecture pour comprendre le mécanisme réel que vous liez en interagissant avec les contrôles.
autres lectures que vous pouvez trouver utiles comprend:
qu'est-Ce qu'Commencer à Appeler
une des règles cardinales de la programmation GUI de Windows est que seule la le thread qui a créé un contrôle peut accéder et / ou modifier son contenu (sauf quelques exceptions documentées). Essayez de le faire à partir de tout autre fil et vous obtiendrez un comportement imprévisible, allant de l'impasse, à exceptions à une demi-assurance-chômage mise à jour. La bonne façon alors de mettre à jour un contrôle à partir d'un autre thread est de poster un message approprié à la file d'attente des messages d'application. Lorsque le message pump se déplace vers l'exécution de ce message, le contrôle sera mis à jour, sur le même le fil qui l'a créé (rappelez-vous, la pompe de message fonctionne sur le fil.)
et, pour un aperçu plus riche en code avec un échantillon représentatif:
opérations Invalides de filetage croisé
// 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 avez une appréciation pour InvokeRequired, vous pouvez envisager d'utiliser une méthode d'extension pour boucler ces appels. C'est bien couverte dans le Débordement de la Pile question Nettoyage du Code Jonché d'Invoquer Nécessaire .
il y a aussi un autre de ce qui s'est passé historiquement qui peut être d'intérêt.
un objet de contrôle ou de fenêtre dans les formes de Windows est juste un enveloppement autour d'une fenêtre Win32 identifiée par un poignée (parfois appelé HWND). La plupart des choses que vous faites avec le contrôle aboutiront éventuellement à un appel D'API Win32 qui utilise cette poignée. La poignée est détenue par le fil qui l'a créé (typiquement le fil principal), et ne doit pas être manipulée par un autre fil. Si pour une raison quelconque vous avez besoin de faire quelque chose avec le contrôle d'un autre thread, vous pouvez utiliser Invoke
pour demander au fil principal de le faire en votre nom.
par exemple, si vous voulez changer le texte d'une étiquette à partir d'un worker thread, vous pouvez faire quelque chose comme ceci:
theLabel.Invoke(new Action(() => theLabel.Text = "hello world from worker thread!"));
si vous voulez modifier un contrôle, il faut le faire dans le thread dans lequel le contrôle a été créé. Cette méthode Invoke
vous permet d'exécuter des méthodes dans le thread associé (le thread qui possède la poignée de fenêtre sous-jacente du contrôle).
dans l'exemple ci-dessous thread1 jette une exception parce que SetText1 essaie de modifier textBox1.Du texte à partir d'un autre thread. Mais dans thread2, L'Action dans SetText2 est exécutée dans le thread dans lequel la TextBox a été créée.
private void btn_Click(object sender, EvenetArgs e)
{
var thread1 = new Thread(SetText1);
var thread2 = new Thread(SetText2);
thread1.Start();
thread2.Start();
}
private void SetText1()
{
textBox1.Text = "Test";
}
private void SetText2()
{
textBox1.Invoke(new Action(() => textBox1.Text = "Test"));
}
Invoke ((MethodInvoker)delegate{ textBox1.Text = "Test";});
en termes pratiques cela signifie que le délégué est garanti pour être invoqué sur le fil principal. Ceci est important car dans le cas des commandes windows si vous ne mettez pas à jour leurs propriétés sur le thread principal alors vous ne voyez pas le changement, ou la commande soulève une exception.
Le motif est:
void OnEvent(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(() => this.OnEvent(sender, e);
return;
}
// do stuff (now you know you are on the main thread)
}
cela signifie que le délégué s'exécute sur le thread UI, même si vous appelez cette méthode d'un travailleur de fond ou thread-pool thread. Les éléments UI ont l'affinité de fil - ils aiment parler directement à un fil: le fil UI. Le thread UI est défini comme le thread qui a créé l'instance de contrôle, et est donc associé à la poignée de fenêtre. Mais tout cela est un détail d'implémentation.
le le point clé est: vous appelleriez cette méthode à partir d'un worker thread de sorte que vous pouvez accéder à L'UI (pour changer la valeur dans une étiquette, etc) - Depuis que vous êtes pas autorisé pour faire cela à partir de tout autre thread que le thread UI.
this.Invoke(delegate)
assurez-vous que vous appelez le délégué l'argument de this.Invoke()
sur le fil principal/fil créé.
je peux dire qu'une règle du pouce n'accède pas à vos contrôles de forme sauf à partir du thread principal.
peut être les lignes suivantes font du sens pour utiliser Invoke ()
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
il y a des situations bien que vous créiez un ThreadPool thread(I. e worker thread) il s'exécute sur le thread principal. Il ne créera pas un nouveau filetage coz le filetage principal est disponible pour le traitement d'autres instructions. Donc tout d'abord examiner si le thread courant est le thread principal en utilisant this.InvokeRequired
si retourne true le code courant tourne sur worker thread alors appeler
ce.Invoke (d, new object[] { text});
sinon mettez directement à jour le contrôle de L'interface utilisateur(Ici vous êtes assurés que vous exécutez le code sur le thread principal.)
délégués sont essentiellement en ligne Action
's ou Func<T>
. Vous pouvez déclarer un délégué en dehors de la portée d'une méthode que vous utilisez ou en utilisant une expression lambda
( =>
); parce que vous exécutez le délégué dans une méthode, vous l'exécutez sur le thread qui est exécuté pour la fenêtre/application courante qui est le bit en gras.
Lambda exemple
int AddFiveToNumber(int number)
{
var d = (int i => i + 5);
d.Invoke(number);
}
cela signifie que le délégué que vous passez est exécuté sur le thread qui a créé l'objet Control (qui est le thread UI).
vous devez appeler cette méthode quand votre application est multi-threadée et vous voulez faire une opération D'UI à partir d'un thread autre que le thread D'UI, parce que si vous essayez juste d'appeler une méthode sur un contrôle à partir d'un thread différent vous obtiendrez un système.InvalidOperationException.