C# несколько источников, разные потоки, один обработчик событий

Мне нужен кто-то с высокими навыками в области потоков и событий.

У меня есть абстрактный класс A и два конкретных класса C1, C2 (например, плагины).

Поскольку мне нужно, чтобы они общались между собой, например, "плагин-приложение", "плагин-плагин", у меня есть метод ExecuteCommand в абстрактном классе, который должен выполнить это. Эта функция поднимает event приложению для обработки определенной команды и возврата результата (например, если одному плагину требуются данные из вызываемого приложения ExecuteCommand из базы и ожидает результата, который поступает с обработчиком событий, обработанным в приложении).

protected object ExecuteCommand(SvcCmdType cmdType, params object[] inputParams)
{
  // this code has been simplified
  SvcCommandEventArgs eventArgs = new SvcCommandEventArgs(cmdType, inputParams);

  // generate processing command event (it requires to fill in the result)
  OnProcessingAppCommand(this, eventArgs);

  return eventArgs.OutputParamsList; 
}

Проблема в том, что:

Если каждый из C1 а также C2 иметь разные темы позади и вызывать одновременно ExecuteCommand изнутри их собственных потоков, тогда наверняка мой дизайн будет сломан, и возвращенный результат будет неожиданным.

Каков лучший дизайн для этого сценария? Я думал использовать внутри ExecuteCommand асинхронные вызовы, такие как использование AsyncOperation... но это правильный путь?

отредактировано: я думаю, что я ищу: синхронный или асинхронный способ лучше для моего сценария? Или я должен обработчик событий приложения обрабатывать в потоке плагина или синхронизировать где-то в моем основном потоке?

Буду очень признателен за хорошее объяснение ваших рекомендаций.

Спасибо.

1 ответ

Решение

Обычный простой метод выполнения потоковой синхронизации на общем ресурсе или блоке кода - использовать мьютекс (или, в данном случае, критическую секцию). Используйте оператор блокировки:

http://msdn.microsoft.com/en-us/library/c5kehkcz(VS.71).aspx

В этой статье говорится о блокировке указателя "this", но это может быть опасно, поскольку внешние вызывающие стороны также могут получить такую ​​же блокировку, что может привести к поломке вашей программы. Сделайте свою блокировку на частной переменной класса.

Вот некоторая модификация вашего примера кода для включения блокировки / критического раздела:

class SomeClass : ISomeInterface
{
  protected object ExecuteCommand(SvcCmdType cmdType, params object[] inputParams)
  {
    lock(executeCommandLock)
    {
      SvcCommandEventArgs eventArgs = new SvcCommandEventArgs(cmdType, inputParams);
      OnProcessingAppCommand(this, eventArgs);
      return eventArgs.OutputParamsList; 
    }
  }

  private Object executeCommandLock = new Object();
}

Редактировать:

(перефразируя из комментариев). Вы упомянули, что вы можете обрабатывать все вызовы ExecuteCommand в одном потоке асинхронно. Вы можете сделать это с помощью класса Dispatcher:

http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.aspx

Получить ссылку на диспетчер в одном потоке. Передайте эту ссылку другим потокам. Когда эти потоки хотят вызвать ExecuteCommand, они используют dispatcher.BeginInvoke. Поскольку они используют BeginInvoke, все вызовы ExecuteCommand будут работать асинхронно, а не блокироваться в этом потоке. Тем не менее, каждая версия ExecuteCommand будет поставлена ​​в очередь и будет последовательно запускать поток диспетчера.

Другие вопросы по тегам