Task (or thread) requires Wait or Join to work
I am trying to make a plugin type system. I have made something along these lines in the past where all plugins execute in the main thread, which leads to problems if the plugins are taking a long time. So I thought I'd execute the appropriate methods in each plugin using Tasks.
I have a main program, which loads each plugin using Assembly.LoadFile, and then reacts to commands a user types. If one of these commands is handled by a plugin (the plugins report what commands they handle, the main program asks when it loads them), my program will start a method in the plugin in its own Task.
Task t = Task.Factory.StartNew(() => Plugin.ProcessCommand(cmd, Params, Context));
Each plugin also implements an event used when it has data to send to the main program for output. The main program attaches a handler to this event when it loads each plugin.
The plugin's ProcessCommand method does whatever work is needed, triggers the OnOutput event, and then ends.
This is a very simple plugin:
public override void ProcessCommand(PluginCommand Command, PluginParameters Parameters, PluginContext Context)
{
OnOutputGenerated(this,"Hello from Plugin A");
}
This worked fine with the first plugin I made. So I created another, using the exact same code, just changing "Hello from Plugin A" to "Hello from Plugin B."
Plugin A always works. If I issue the appropriate command in the main program, it runs and says Hello from Plugin A. Great.
The problem is: Plugin B executes maybe one in every 30 attempts. I've discovered, however, that if call the plugin in the following way, it works every time:
Task t = Task.Factory.StartNew(() => Plugin.ProcessCommand(cmd, Params, Context));
t.Wait(100);
Is there a technical reason why this might help? I've read through pretty much all of http://www.albahari.com/threading/ trying to figure things out, but I've not had any luck.
It's worth noting that I've also done this with threads, with the same problem.
Thread t = new Thread(() => Plugin.ProcessCommand(cmd, Params, Context));
t.Start();
Adding:
t.Join(100);
"fixed" it.
Updated
I've simplified everything. I've made a new project, which strips out all the code unrelated to the 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 PluginManag开发者_运维技巧er.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);
}
The main issue has changed. Previously, one of the plugins didn't work most of the time. Imagine two plugins.
Plugin A
* Has one command: CommandA * Command triggers OnOutputGenerated event with the string "Hello from Plugin A"Plugin B
* Has one command: CommandB * Command triggers OnOutputGenerated event with the string "Hello from Plugin B"If I run this new project I've made, and issue the command "CommandA", it will return "Hello from Plugin B". It continues doing this until I actually issue "CommandB". Once I've done that, it prints "Hello from Plugin B" (as it should). If I then issue "CommandA" again, it returns "Hello from Plugin A" (as it should have originally).
If I add
t.Wait(100);
it's fixed. It still seems to be related to the Task somehow, but I'm at a loss to explain how. It would appear that my logic otherwise is fine. I can't see how it would execute Plugin B when it should execute Plugin A, or vice-versa.
It sounds like without the Wait
or Join
, your main program simply exits before the requested Task
code has a chance to run. If the Task
logic used to be inline in the main thread, that would have implied the main thread would wait while the code executed. Now you have moved it to a separate thread, you have to add an explicit wait to allow every Task
you start up to complete (maybe with a timeout, in case something goes wrong).
It's possible that even if you don't wait, a Task
could occasionally finish up - that's going to be indeterminate, depending on the timing in any individual run.
Can you clarify what happens in the main thread without the Wait
or Join
?
精彩评论