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 будет поставлена в очередь и будет последовательно запускать поток диспетчера.