Задача (или поток) требует подождать или присоединиться к работе - PullRequest
2 голосов
/ 05 декабря 2010

Я пытаюсь создать систему плагинов. Я сделал что-то подобное в прошлом, когда все плагины выполняются в основном потоке, что приводит к проблемам, если плагины занимают много времени. Поэтому я подумал, что буду выполнять соответствующие методы в каждом плагине с помощью задач.

У меня есть основная программа, которая загружает каждый плагин с помощью 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, или наоборот.

1 Ответ

2 голосов
/ 05 декабря 2010

Похоже, что без Wait или Join ваша основная программа просто завершает работу до того, как запрошенный код Task сможет запустить. Если бы логика Task была встроенной в главном потоке, это означало бы, что основной поток будет ожидать выполнения кода. Теперь вы переместили его в отдельный поток, вам нужно добавить явное ожидание, чтобы каждый Task, который вы запускаете, завершился (возможно, с таймаутом, если что-то пойдет не так).

Возможно, что даже если вы не будете ждать, Task может иногда закончить - это будет неопределенным, в зависимости от времени в каждом отдельном заезде.

Можете ли вы уточнить, что происходит в главном потоке без Wait или Join?

...