2009-05-21 6 views
2

J'ai une question sur les événements dans .NET (C#). J'ai dû écrire du code pour plusieurs situations dans lesquelles une tâche d'arrière-plan est en cours d'exécution et je souhaite notifier un thread principal, ou une classe de contrôleur, que quelque chose s'est produit, comme une tâche terminée ou une copie de fichier terminée. Je ne veux pas que la tâche en arrière-plan attende que le délégué du thread principal traite l'événement. Je veux faire quelque chose comme le passage de message: Envoyer le message, mais qui se soucie de ce qu'ils font avec lui.Comment puis-je déclencher un événement sans attendre l'exécution des écouteurs d'événement?

Par exemple:

Une classe est écrit pour traiter plusieurs processus dans l'ordre, l'un après l'autre. Chaque processus doit être exécuté sur un thread d'arrière-plan. Lorsque le travail est terminé, un événement se déclenche et indique au contrôleur que c'est fait (disons en utilisant la méthode OnCompleted())

Le problème est que si le gestionnaire d'événements du contrôleur est utilisé pour démarrer le processus suivant, les processus précédents 'OnComplete La méthode reste sur la pile des appels (ne finit jamais l'exécution) jusqu'à ce que tous les processus soient terminés.

Dans cette situation, comment la tâche d'arrière-plan peut-elle informer la classe de contrôleur que le travail est effectué sans conserver la méthode de déclenchement d'événement sur la pile?

Exemple 2: Un programme de sauvegarde.

Un fil d'arrière-plan s'exécute pour copier chaque fichier vers la destination. L'arrière-plan doit notifier l'interface utilisateur du dernier fichier qui a été copié, mais il n'a pas besoin d'attendre la mise à jour de l'interface utilisateur. Au lieu de cela, il veut juste dire, "BTW, voici quelques informations. Maintenant, laissez-moi retourner au travail." L'écouteur d'événement ne doit pas bloquer le traitement de l'événement.

Répondre

2

Vous pouvez faire une Invoke async lorsque l'événement est (comme mentionné), ou simplement soulever l'événement lui-même sur un fil de fond:

void OnUpdated(EventArgs e) { 
    EventHandler h = this.Updated; 
    if (h != null) h(e); 
} 

void DoStuff() { 
    BigMethod(); 
    ThreadPool.QueueUserWorkItem(OnUpdated, EventArgs.Empty); 
    BigMethod2(); 
} 

Si vous augmentez de manière asynchrone, plusieurs auditeurs seraient le traitement de votre événement en même temps. À tout le moins, cela nécessite une classe EventArg thread-safe.Si vous vous attendez à ce qu'ils interagissent également avec votre classe, vous devez documenter très attentivement ou bien le rendre sûr pour les threads. Augmenter l'événement sur un thread d'arrière-plan entraîne les mêmes mises en garde pour vos méthodes de classe, mais vous n'avez pas à vous soucier de la classe EventArgs elle-même.

1

Si le premier événement ne fait que lancer le fil, peu importe les autres écouteurs d'événement.

+0

Comment pourrais-je faire cela en C#? –

+0

Eh bien, vous semblez savoir comment lancer un autre thread. Il suffit au premier écouteur d'événement de le faire (dès que vous créez l'objet avec l'événement, affectez le gestionnaire –

3

Il semble que vous essayez d'appeler les délégués dans la liste d'appels de l'événement de manière asynchrone.

Je vous suggère de lire .NET Asynchronous Events To Send Process Status To User Interface:

Le .NET Framework nous offre le concept de lever les événements (et d'autres articles) dans nos classes de manière asynchrone. Cela signifie que nous pouvons relever l'événement de telle manière à ne pas avoir l'abonné à cet événement (généralement l'interface utilisateur) occupent le traitement dans la méthode qui a soulevé l'événement. L'avantage étant qu'il n'a pas un impact négatif sur les performances de notre méthode de couche de gestion .

+0

que j'avais lu au sujet du modèle BeginInvoke/EndInvoke, mais je pensais à l'utiliser du côté de l'écouteur, pas sur côté de lancement Y aurait-il un problème s'il y avait des tonnes d'appels BeginInvoke en suspens? –

0

Pour votre cas 2 du programme de sauvegarde. L'exemple de code déclenchera une copie de fichier de manière asynchrone et une fois la copie terminée, elle appelle la méthode de rappel. Dans le rappel si vous ne voulez pas attendre pour l'interface utilisateur de mettre à jour, vous devrez appeler le code de mise à jour de l'interface utilisateur de manière asynchrone

Vous pouvez utiliser des délégués asynchrones

public class AsyncFileCopier 
    { 
     public delegate void FileCopyDelegate(string sourceFile, string destFile); 

     public static void AsynFileCopy(string sourceFile, string destFile) 
     { 
      FileCopyDelegate del = new FileCopyDelegate(FileCopy); 
      IAsyncResult result = del.BeginInvoke(sourceFile, destFile, CallBackAfterFileCopied, null); 
     } 

     public static void FileCopy(string sourceFile, string destFile) 
     { 
      // Code to copy the file 
     } 

     public static void CallBackAfterFileCopied(IAsyncResult result) 
     { 
      // Notify UI by calling an async del (probably using fire & forget approach or another callback if desired) 
     } 
    } 

Vous pouvez l'appeler comme:

AsyncFileCopier.AsynFileCopy("abc.txt", "xyz.txt"); 

Ce link vous indique les différentes techniques de codage asyn