2010-12-05 40 views
2

J'essaie de faire un système de type plugin. J'ai fait quelque chose dans le passé où tous les plugins s'exécutent dans le thread principal, ce qui entraîne des problèmes si les plugins prennent beaucoup de temps. J'ai donc pensé que j'exécuterais les méthodes appropriées dans chaque plugin en utilisant les tâches.Tâche (ou thread) nécessite attendre ou rejoindre pour travailler

J'ai un programme principal, qui charge chaque plugin en utilisant Assembly.LoadFile, puis réagit aux commandes que l'utilisateur tape. Si l'une de ces commandes est gérée par un plugin (les plugins signalent quelles commandes ils gèrent, le programme principal demande quand il les charge), mon programme démarrera une méthode dans le plugin dans sa propre tâche. Chaque plugin implémente également un événement utilisé lorsqu'il a des données à envoyer au programme principal pour la sortie. Le programme principal attache un gestionnaire à cet événement lorsqu'il charge chaque plugin.

La méthode ProcessCommand du plugin effectue le travail nécessaire, déclenche l'événement OnOutput, puis se termine.

Ceci est un plugin très simple:

public override void ProcessCommand(PluginCommand Command, PluginParameters Parameters, PluginContext Context) 
{ 
    OnOutputGenerated(this,"Hello from Plugin A"); 
} 

Cela a bien fonctionné avec le premier plug-in que j'ai fait. J'ai donc créé un autre, en utilisant exactement le même code, en changeant juste "Bonjour du Plugin A" à "Bonjour du Plugin B."

Le plugin A fonctionne toujours. Si j'émets la commande appropriée dans le programme principal, il s'exécute et dit Bonjour de Plugin A. Great.

Le problème est le suivant: Le plugin B s'exécute peut-être une fois toutes les 30 tentatives. Je l'ai découvert, cependant, que si appeler le plug-in de la manière suivante, il fonctionne à chaque fois:

Task t = Task.Factory.StartNew(() => Plugin.ProcessCommand(cmd, Params, Context)); 
t.Wait(100); 

Y at-il une raison technique pourrait aider? J'ai lu à peu près tout de http://www.albahari.com/threading/ essayant de comprendre les choses, mais je n'ai pas eu de chance.

Il est à noter que j'ai également fait cela avec des threads, avec le même problème.

Thread t = new Thread(() => Plugin.ProcessCommand(cmd, Params, Context)); 
t.Start(); 

Ajout:

t.Join(100); 

"fixe" il.

Mise à jour

J'ai tout simplifié. J'ai fait un nouveau projet, qui supprime tout le code sans rapport avec les bugs.

foreach (string File in Directory.GetFiles(PluginDir, "*.dll")) { 

    try { 

     IPlugin Plugin = PluginManager.LoadPlugin(File); 
     Plugin.OnOutputGenerated += new PluginOutputEvent(Plugin_OnOutputGenerated); 

    } catch (Exception ex) { 

    } 

} 

// main loop 

string Line = Console.ReadLine(); 

foreach (IPlugin Plugin in PluginManager.LoadedPlugins) { 

    foreach (PluginCommand cmd in Plugin.GetCommands()) { 

     if (cmd.Command.Equals(Line, StringComparison.InvariantCultureIgnoreCase)) { 

      PluginParameters Params = cmd.TryParseParameters(ParamString); 
      Task t = Task.Factory.StartNew(() => Plugin.ProcessCommand(cmd, Params, Context)); 

     } 

    } 

} 

// output handler 

static void Plugin_OnOutputGenerated(IPlugin Plugin, string OutputString) { 

    Console.WriteLine("Output: " + OutputString); 

} 

Le problème principal a changé. Auparavant, l'un des plugins ne fonctionnait pas la plupart du temps. Imaginez deux plugins.

Plugin A
* A une commande: Commanda
* Commande déclenche l'événement OnOutputGenerated avec la chaîne "Bonjour du plugin A"

Plugin B
* A une commande: CommandB
* La commande déclenche l'événement OnOutputGenerated avec la chaîne "Hello from Plugin B"

Si je lance ce nouveau projet, j'ai fait, et émettez la commande "CommandA", il retournera "Bonjour du Plugin B". Cela continue jusqu'à ce que j'émette réellement "CommandB". Une fois que j'ai fait cela, il affiche "Hello from Plugin B" (comme il se doit). Si je répète "CommandA", il renvoie "Hello from Plugin A" (comme il aurait dû l'être à l'origine).

Si j'ajoute

t.Wait(100); 

il est fixe. Cela semble toujours lié à la tâche, mais je n'arrive pas à expliquer comment. Il semblerait que ma logique soit bonne sinon. Je ne vois pas comment il exécuterait le Plugin B lorsqu'il devrait exécuter le Plugin A, ou vice-versa.

+0

Se pourrait-il que votre variable de tâche t soit hors de portée avant qu'elle se termine? –

Répondre

2

Il semble que sans le Wait ou Join, votre programme principal se termine simplement avant que le code Task demandé ait une chance de s'exécuter. Si la logique Task était utilisée dans le thread principal, cela impliquerait que le thread principal attendrait pendant l'exécution du code. Maintenant que vous l'avez déplacé vers un thread séparé, vous devez ajouter une attente explicite pour autoriser tous les Task que vous commencez à terminer (peut-être avec un timeout, au cas où quelque chose se passe mal).

Il est possible que même si vous n'attendez pas, un Task peut occasionnellement finir - cela va être indéterminé, en fonction du timing de chaque course individuelle. Pouvez-vous clarifier ce qui se passe dans le fil principal sans les Wait ou Join?

+0

Pour toutes fins utiles, le programme principal est une grande boucle avec une Console.ReadLine(), une section pour analyser tout ce qui est tapé, et ensuite la section qui appelle le plugin si l'utilisateur a tapé quelque chose de approprié à un plugin. L'intention est de permettre à l'utilisateur de déclencher un certain nombre de tâches potentiellement longues sans avoir à attendre que le programme revienne à Console.ReadLine. – Neil

+0

Merci - vos gestionnaires d'événements d'achèvement pourraient-ils être hors de portée comme indiqué par @Jakob dans les commentaires, lorsque vous n'attendez pas/ne vous joignez pas? –

+0

Vraiment pas sûr à ce stade. Ça ne me ressemble pas, mais j'essaie différentes choses. Je fournirai plus de code quand j'aurai compris exactement ce qui m'arrive! Obtenir environ trois bugs différents en ce moment. J'essaie de comprendre ce qui est réellement pertinent. – Neil