2017-10-06 9 views
2

Compte tenu du C# exemple de code:Comment exécuter gestionnaire d'événements sur thread cible

using System; 
using System.Threading; 
using System.Windows.Forms; 

public class MnFrm : Form 
{ 
    private void MnFrm_Load(Object sender, EventArgs e) 
    { 
     this.WorkCompleted += MnFrm_WorkCompleted; 
    } 

    private void btn_Click(Object sender, EventArgs e) 
    { 
     ThreadPool.QueueUserWorkItem(AsyncMethod); 
    } 

    private void MnFrm_WorkCompleted(Object sender, Boolean e) 
    { 
     MessageBox.Show("Work completed"); 
    } 

    private void AsyncMethod(Object state) 
    { 
     // Do stuff 
     Boolean result = true; // just as an example 
     WorkCompleted?.Invoke(this, result); 
    } 

    private event EventHandler<Boolean> WorkCompleted; 
} 

Lorsque l'utilisateur clique sur le bouton btn la méthode AsyncMethod est exécutée sur un autre thread géré par le ThreadPool. Après un certain temps, le travail est terminé et le résultat est affiché via un autre événement. Ce gestionnaire d'événements (WorkCompleted) s'exécute sur le thread utilisé pour exécuter AsyncMethod car lorsque l'application est exécutée, vous obtenez l'exception "Cross-Threading".

La question est donc de savoir comment exécuter le gestionnaire d'événements MnFrm_WorkCompleted sur le thread UI?

+0

La solution correcte dépend de ce que 'AsyncMethod' est réellement en train de faire.Si la méthode accède à des ressources externes, alors votre code peut être modifié pour fonctionner efficacement sur un thread sans se soucier des threads/invoke et d'autres problèmes liés au multithread. – Fabio

+0

Pourquoi utilisez-vous 'QueueUserWorkItem' au lieu de' var result = wait Task.Run (...) '? Il est disponible dans toutes les versions .NET prises en charge et convertit tout ce code en une ligne * simple * qui gère correctement les opérations asynchrones. La première version .NET prise en charge est 4.5.2. 'await' a été ajouté plus tôt, en 4.5. Tâches dans 4.0 –

+2

Même si vous devez signaler quelque chose à partir d'un autre thread, n'utilisez pas d'événements. C'est le travail de l'interface IProgress et de la classe 'Progresss ' –

Répondre

0

Si vous changez à ceci:

this.Invoke(new Action(() => 
{ 
    WorkCompleted?.Invoke(this, result); 
}); 

Il fonctionnera. C'est parce que le formulaire contient une méthode Invoke() qui invoquera la méthode sur le thread sur lequel il a été créé. L'appel d'un événement n'est rien de plus que l'appel du délégué.

Lire here formulaire moet info.

Vous pouvez utiliser le InvokeRequired pour déterminer si vous êtes sur le bon fil, mais je pense que c'est au-dessus et le rend moins lisible. Utilisez toujours this.Invoke.


BeginInvoke, c'est utile lorsque l'affichage '' un message au thread d'interface utilisateur. Le seul problème est que lorsque le thread soulève de nombreux événements et que le thread de l'interface utilisateur n'a pas assez de temps pour les traiter, vous ne pouvez pas voir combien sont mis en file d'attente.


Une troisième méthode utilise une file d'attente. Lorsque l'événement se produit, ajoutez un message à une file d'attente (j'utilise un List<>) et utilisez un minuteur de formulaire pour traiter la file d'attente. L'avantage est que le thread n'est pas bloqué par le thread de l'interface utilisateur et continue directement après l'avoir ajouté à la file d'attente. Et votre application sera bloquée.

2

Vous pouvez utiliser les méthodes Control.Invoke ou Control.BeginInvoke pour appeler une méthode particulière sur un thread d'interface utilisateur. Essayez le code ci-dessous:

private void MnFrm_WorkCompleted(Object sender, Boolean e) 
{ 
    if (InvokeRequired) 
    { 
     Invoke((Action) (() => MnFrm_WorkCompleted(sender, e))); 
     return; 
    } 
    MessageBox.Show("Work completed"); 
} 

Pour différence entre Invoke Et BeginInvoke: What's the difference between Invoke() and BeginInvoke()

0

Merci à tous!

La réponse de Jeroen van Langen est tout à fait correct! Ça a marché. C'était en fait comment je l'ai fait, mais je ne voulais pas afficher la solution afin d'éviter les préjugés - je voulais voir des solutions alternatives.

Cependant, je préfère la réponse de Panagiotis Kanavos où il a suggéré d'utiliser var result=await Task.Run(...). Cela a nettoyé le code magnifiquement! Merci Panagiotis Kanavos !!!