Я пытаюсь создать систему плагинов. Я сделал что-то подобное в прошлом, когда все плагины выполняются в основном потоке, что приводит к проблемам, если плагины занимают много времени. Поэтому я подумал, что буду выполнять соответствующие методы в каждом плагине с помощью задач.
У меня есть основная программа, которая загружает каждый плагин с помощью Assembly.LoadFile, а затем реагирует на команды, введенные пользователем. Если одна из этих команд обрабатывается плагином (плагины сообщают, какие команды они обрабатывают, основная программа спрашивает, когда загружает их), моя программа запустит метод в плагине в своей собственной задаче.
Task t = Task.Factory.StartNew(() => Plugin.ProcessCommand(cmd, Params, Context));
Каждый плагин также реализует событие, используемое, когда у него есть данные для отправки в основную программу для вывода. Основная программа прикрепляет обработчик к этому событию при загрузке каждого плагина.
Метод ProcessCommand плагина выполняет любую необходимую работу, инициирует событие OnOutput и затем завершается.
Это очень простой плагин:
public override void ProcessCommand(PluginCommand Command, PluginParameters Parameters, PluginContext Context)
{
OnOutputGenerated(this,"Hello from Plugin A");
}
Это работало нормально с первым плагином, который я сделал. Поэтому я создал другой, используя точно такой же код, просто изменив «Hello from Plugin A» на «Hello from Plugin B.»
Плагин А всегда работает. Если я запускаю соответствующую команду в основной программе, она запускается и говорит Привет из плагина А. Отлично.
Проблема в том, что плагин B выполняет, возможно, одну из каждых 30 попыток. Однако я обнаружил, что если вызывать плагин следующим образом, он работает каждый раз:
Task t = Task.Factory.StartNew(() => Plugin.ProcessCommand(cmd, Params, Context));
t.Wait(100);
Есть ли техническая причина, по которой это может помочь? Я прочитал почти все http://www.albahari.com/threading/, пытаясь разобраться, но мне не повезло.
Стоит отметить, что я также делал это с потоками, с той же проблемой.
Thread t = new Thread(() => Plugin.ProcessCommand(cmd, Params, Context));
t.Start();
Добавление:
t.Join(100);
«исправил» это.
Обновлено
Я все упростил. Я сделал новый проект, который убирает весь код, не связанный с ошибками.
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);
}
Основная проблема изменилась. Ранее один из плагинов не работал большую часть времени. Представьте себе два плагина.
Плагин A
* Имеет одну команду: CommandA
* Команда запускает событие OnOutputGenerated со строкой «Hello from Plugin A»
Плагин B
* Имеет одну команду: CommandB
* Команда запускает событие OnOutputGenerated со строкой «Hello from Plugin B»
Если я запустил этот новый проект и произвел команду «CommandA», он вернет «Hello from Plugin B». Это продолжается до тех пор, пока я не выдам «CommandB». Как только я это сделаю, он выводит «Hello from Plugin B» (как и должно быть). Если я затем снова выдаю «CommandA», он возвращает «Hello from Plugin A» (как и должно было быть изначально).
Если я добавлю
t.Wait(100);
это исправлено. Кажется, это все еще как-то связано с Задачей, но я затрудняюсь объяснить, как это сделать. Казалось бы, моя логика в противном случае в порядке. Я не вижу, как он будет выполнять плагин B, когда он должен выполнить плагин A, или наоборот.