web-dev-qa-db-fra.com

Invoke (délégué)

Quelqu'un peut-il s'il vous plaît expliquer cette déclaration écrite sur ce lien

Invoke(Delegate):

Exécute le délégué spécifié sur le thread qui possède le handle de fenêtre sous-jacent du contrôle .

Quelqu'un peut-il expliquer ce que cela signifie (surtout le plus audacieux)? Je ne parviens pas à comprendre clairement

78
user1903439

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 fil sécuritaire. 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 marshal l'appel au bon fil. Cette propriété peut être utilisée pour Déterminez si vous devez appeler une méthode invoke, ce qui peut être utile si vous ne savez pas quel fil possède un contrôle.

De (Control.InvokeRequired } _

Effectivement, Invoke veille à ce que le code que vous appelez apparaisse sur le thread sur lequel le contrôle est "vivant", empêchant ainsi les exceptions multithreads.

D'un point de vue historique, dans .Net 1.1, cela était effectivement autorisé. Cela signifiait que vous pouviez essayer d'exécuter du code sur le fil "GUI" à partir de n'importe quel fil d'arrière-plan et que cela fonctionnerait pour la plupart. Parfois, cela ferait simplement quitter votre application parce que vous interrompiez efficacement le fil de l'interface graphique pendant qu'il faisait autre chose. C'est l'exception Cross Threaded Exception - imaginez essayer de mettre à jour une zone de texte pendant que l'interface graphique dessine autre chose. 

  • Quelle action est prioritaire? 
  • Est-il même possible que les deux se produisent en même temps? 
  • Qu'advient-il de toutes les autres commandes que l'interface graphique doit exécuter?

Effectivement, vous interrompez une file d'attente, ce qui peut avoir de nombreuses conséquences imprévues. Invoke est en fait le moyen "poli" de placer ce que vous voulez faire dans cette file d'attente. Cette règle a été appliquée à partir de .Net 2.0 via une exception InvalidOperationException levée.

Pour comprendre ce qui se passe réellement dans les coulisses et ce que l'on entend par "Thread GUI", il est utile de comprendre ce qu'est un Message Pump ou un Message Boucle. 

La question " Qu'est-ce qu'une pompe à message " est-elle déjà une réponse, et cette lecture est recommandée pour comprendre le mécanisme réel auquel vous vous attachez lors de l'interaction avec des contrôles.

Autres lectures que vous pouvez trouver utiles comprend:

Quoi de neuf avec Begin Invoke

L’une des règles fondamentales de la programmation par l’interface graphique Windows est que seul le fichier Le fil 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 n'importe quel autre fil et vous aurez un comportement imprévisible allant de blocage, à exceptions à une demi-interface utilisateur mise à jour. La bonne façon alors de mettre à jour un fichier Le contrôle à partir d’un autre thread consiste à publier un message approprié dans le fichier file d'attente de messages d'application. Lorsque le message pompe circule à En exécutant ce message, le contrôle sera mis à jour, sur le même le thread qui l'a créé (rappelez-vous que la pompe à messages fonctionne sur le thread principal ).

et, pour un aperçu plus dense en code avec un échantillon représentatif:

Opérations inter-threads 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 avez compris InvokeRequired, vous pouvez envisager d’utiliser une méthode d’extension pour encapsuler ces appels. Ceci est habilement abordé dans la question de débordement de pile Nettoyage du code jonché d'invocation requise

Il y a aussi un autre { récit de ce qui s'est passé historiquement } qui pourrait être intéressant.

115
dash

Un objet de contrôle ou de fenêtre dans Windows Forms est simplement un wrapper autour d'une fenêtre Win32 identifiée par un handle (parfois appelé HWND). La plupart des opérations effectuées avec le contrôle aboutiront éventuellement à un appel de l'API Win32 utilisant ce descripteur. Le descripteur appartient au thread qui l'a créé (généralement le thread principal) et ne doit pas être manipulé par un autre thread. Si, pour une raison quelconque, vous devez utiliser le contrôle depuis un autre thread, vous pouvez utiliser Invoke pour demander au thread principal de le faire en votre nom.

Par exemple, si vous souhaitez modifier le texte d'une étiquette à partir d'un thread de travail, vous pouvez procéder de la manière suivante:

theLabel.Invoke(new Action(() => theLabel.Text = "hello world from worker thread!"));
61
Thomas Levesque

Si vous souhaitez modifier un contrôle, vous devez 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 le handle de fenêtre sous-jacent du contrôle).

Dans l'exemple ci-dessous, exemple de thread1 lève une exception car SetText1 tente de modifier textBox1.Text à partir d'un autre thread. Mais dans thread2, Action dans SetText2 est exécutée dans le thread dans lequel la zone de texte 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"));
}
20
Mehmet Ataş
Invoke((MethodInvoker)delegate{ textBox1.Text = "Test"; });
5
BestDeveloper

this.Invoke(delegate) assurez-vous d'appeler le délégué l'argument à this.Invoke() sur le thread principal/créé. 

Je peux dire qu'une règle Thumb n'accède pas à vos contrôles de formulaire, sauf à partir du fil principal.

Peut-être que les lignes suivantes ont un 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 existe des situations bien que vous créiez un thread Threadpool (i.e worker) qui s’exécutera sur le thread principal. Il ne créera pas de nouveau thread car le thread principal est disponible pour le traitement d'instructions supplémentaires. Donc, vérifiez d'abord si le thread en cours d'exécution est le thread principal en utilisant this.InvokeRequired si renvoie vrai si le code actuel est en cours d'exécution sur le thread de travail, appelez donc this.Invoke (d, nouvel objet [] {texte});

sinon, mettez directement à jour le contrôle de l'interface utilisateur (vous avez la garantie d'exécuter le code sur le thread principal.)

2
Srikanth

Concrètement, cela signifie que le délégué est invoqué sur le thread principal. Ceci est important car dans le cas des contrôles Windows, si vous ne mettez pas à jour leurs propriétés sur le thread principal, vous ne voyez pas le changement ou le contrôle génère 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)
}
2
briantyler

Cela signifie que le délégué s'exécutera sur le thread d'interface utilisateur, même si vous appelez cette méthode à partir d'un travailleur en arrière-plan ou d'un thread de pool de threads. Les éléments de l'interface utilisateur ont l'affinité thread - ils n'aiment que parler directement à un seul thread: le thread de l'interface utilisateur. Le thread de l'interface utilisateur est défini comme le thread qui a créé l'instance de contrôle et est donc associé au handle de fenêtre. Mais tout cela est un détail de mise en œuvre.

Le point clé est le suivant: vous appelez cette méthode à partir d'un thread de travail pour pouvoir accéder à l'interface utilisateur (pour changer la valeur d'un libellé, etc.) - puisque vous êtesnon autorisépour le faire à partir tout autre thread que le thread d'interface utilisateur.

1
Marc Gravell

Les délégués sont essentiellement des Action 's ou Func<T> en ligne. Vous pouvez déclarer un délégué en dehors de la portée d'une méthode que vous exécutez ou en utilisant une expression lambda (=>); Comme vous exécutez le délégué dans une méthode, vous l'exécutez sur le thread en cours d'exécution pour la fenêtre/l'application en cours, qui est le bit en gras.

Exemple Lambda

int AddFiveToNumber(int number)
{
  var d = (int i => i + 5);
  d.Invoke(number);
}
0
LukeHennerley

Cela signifie que le délégué que vous transmettez est exécuté sur le thread qui a créé l'objet Control (qui est le thread d'interface utilisateur).

Vous devez appeler cette méthode lorsque votre application est multi-thread et que vous souhaitez effectuer une opération d'interface utilisateur à partir d'un thread autre que celui de l'interface utilisateur, car si vous essayez simplement d'appeler une méthode sur un contrôle à partir d'un autre thread, vous obtiendrez une System.InvalidOperationException.

0
user1610015