5 votes

Pourquoi n'ai-je pas l'erreur "Cross-thread operation not valid" ?

J'utilise un BackgroundWorker et je fais cela :

private void loadNewAsyncToolStripMenuItem_Click(object sender, EventArgs e)
{
    this.Text = "RunWorkerAsync()";
    backgroundWorkerLoading.RunWorkerAsync();
}
private void backgroundWorkerLoading_DoWork(object sender, DoWorkEventArgs e)
{
    UnsafeThreadMethod("hello");
    EvenUnsaferThreadMethod();
}

Et maintenant les deux méthodes.

private void UnsafeThreadMethod(string text)
{
    toolStripLabelRssFeedData.Text = text;
}

private void EvenUnsaferThreadMethod()
{
    panelLoading.Visible = true;
}

Je ne comprends pas pourquoi UnsafeThreadMethod ne lève pas l'exception suivante mais EvenUnsaferThreadMethod fait.

L'opération inter-filière n'est pas valide : Le contrôle 'panelLoading' a été accédé à partir d'un thread autre que le > thread sur lequel il a été créé.

Selon le message, c'est parce que toolStripLabelRssFeedData a été créé sur le même fil mais il ne l'a pas été.

Je pensais que je ne pouvais pas appeler les contrôles créés par le thread principal et que je devais utiliser la fonction ProgressChanged événement. Que se passe-t-il ?

Et j'ai une deuxième question. Quel est l'avantage de faire comme ça alors que je peux utiliser ProgressChanged ? Que dois-je faire ?

private void EvenUnsaferThreadMethod()
{
    if (panelLoading.InvokeRequired)
    {
        panelLoading.Invoke(new MethodInvoker(() => { EvenUnsaferThreadMethod(); }));
    }
    else
    {
        panelLoading.Visible = true;
    }
}

5voto

Henk Holterman Points 153608

A la première question :

  • l'exception cross-thread est délibérément lancée en mode Debug. Cela signifie qu'un contrôle de code (conditionnel) sur InvokeRequired est intégré dans la plupart des contrôles GUI. Comme le Panel.

  • Apparemment, le ToolstripLabel n'effectue pas cette vérification. Puisqu'il ne dérive pas de Control, cela pourrait être dû au fait qu'il est en dehors de la portée de ce filet de sécurité.

Étant donné que l'avertissement standard "Any instance members are not guaranteed to be thread safe" s'applique au ToolstripLabel, je me contenterais de la logique normale InvokeRequired pour définir le texte.

3voto

Justin Pihony Points 21088

Pour votre première question, je ne suis pas tout à fait sûr, mais un examen en ligne semble montrer que, parfois, cela ne lève pas une exception, mais ne met pas à jour l'étiquette. Est-ce le cas ici ? Votre étiquette est mise à jour sans qu'il y ait d'exception ?

Cependant, je peux répondre à votre deuxième question dès maintenant. Le site ProgressChanged est destiné exactement à ce à quoi il ressemble. Il est censé être appelé pour faire connaître au thread de l'interface utilisateur l'état du travailleur d'arrière-plan afin qu'il puisse se mettre à jour de manière appropriée. Le thread d'appel original (l'interface utilisateur dans ce cas) est celui qui est utilisé pour l'événement ProgressChanged ainsi, lorsqu'il est mis à jour, il n'a pas besoin d'appeler Invoke . Mais cela ne devrait vraiment être fait que pour montrer la progression d'un travailleur en arrière-plan.

Maintenant, si ce n'est pas une mise à jour que vous essayez de transmettre à la méthode d'appel, alors je suggérerais de simplement renvoyer vos données de retour par le biais de l'élément RunWorkerCompleted événement. Cela permet de transmettre toutes les données finales au thread (UI) d'origine, afin qu'il puisse mettre à jour l'interface utilisateur sans qu'il soit nécessaire d'utiliser l'événement Invoke .

Alors, oui, votre appel à Invoke fonctionnera, cependant. Cependant, comprendre à quoi sert chacun des autres événements peut vous aider à comprendre pourquoi utiliser une méthode plutôt qu'une autre. Peut-être qu'un ProgressChanged l'événement s'adapte mieux ? Cela peut également désencombrer votre code en évitant les invocations inutiles.

Mise à jour du premier q

Je ne trouve toujours rien sur le fait que la barre d'outils n'a pas besoin de l'invoke. En fait, je trouve le contraire en utilisant des recherches sur Google comme "toolstriplabel no cross thread exception" ou "toolstriplabel invoke", etc. Cependant, comme henk l'a mentionné, le toolstriplabel n'hérite pas de control, ce qui pourrait expliquer pourquoi aucun invoke n'est nécessaire. Cependant, ma suggestion est de supposer qu'il se comportera comme n'importe quel autre contrôle d'interface utilisateur et de s'assurer qu'il est mis à jour sur le thread de l'interface utilisateur pour être sûr. ne pas se fier aux bizarreries . Mieux vaut prévenir que guérir, on ne sait jamais si ce genre de choses peut changer, d'autant plus qu'il s'agit logiquement d'un élément d'interface utilisateur pour la plupart..,

0voto

Tigran Points 41381

L'avantage de votre deuxième choix est qu'il travaux :)

Tous les éléments de l'interface utilisateur sont créés sur le fil d'exécution principal de l'interface utilisateur et, ce qui est plus important du point de vue de cette question, c'est qu'ils peuvent être accessible uniquement dans ce fil .

C'est la raison pour laquelle votre premièrement l'affaire échoue et c'est la raison pour laquelle votre deuxième de l'affaire fonctionnera. Invoke()... redirigera l'appel merhod requis vers le thread principal de l'interface utilisateur.

J'espère que cela vous aidera.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X