none
Как запустить обработчик события в новом потоке? RRS feed

  • Вопрос

  • У меня есть событие, но его обрабочик выполняется в том же потоке, в котором это событие генерируется. Как сделать так, чтобы выполнение шло в другом потоке, чтобы разгрузить поток, поднимающий событие?
     
     /// <summary>
     /// Событие прихода данных.
     /// </summary>
     public event EventHandler<NewDataEventArgs> NewDataEvent; 
    
     /// <summary>
     /// Метод для поднятия события прихода данных из производных классов.
     /// </summary>
     protected virtual void OnNewDataEvent(List<D> updateD)
     {
      var handler = NewDataEvent;
      if (handler != null)
      handler(this, new NewDataEventArgs(updateD));
     }
    
     /// <summary>
     /// Инкапсулирует параметры, передаваемые при поднятии события прихода данных.
     /// </summary>
     public class NewDataEventArgs : EventArgs
     {
      public NewDataEventArgs(List<D> updateD)
      {
       UpdateD = updateD;
      }
    
      public List<D> UpdateD { get; set; }
     }
    


    27 июля 2011 г. 17:17

Ответы

  • protected virtual void OnNewDataEvent(List<D> updateD)
    {
       var handler = NewDataEvent;
    
       var ud = updateD;
       if (handler != null)
         ThreadPool.QueueUserWorkItem((st) => handler(this, new NewDataEventArgs(ud)));
    }
    
    
    

    • Помечено в качестве ответа Qwester33 27 июля 2011 г. 19:07
    27 июля 2011 г. 18:34
    Отвечающий
  • Круто.

    Вроде работает, даже если убрать:

     

     var ud = updateD;
     if (handler != null)
     ThreadPool.QueueUserWorkItem((st) => handler(this, new NewDataEventArgs(ud)));
    

    и сделать просто:

     

     if (handler != null)
     ThreadPool.QueueUserWorkItem((st) => handler(this, new NewDataEventArgs(updateD)));
    

     

    Так тоже можно? Я почему уточняю, потомоу что у меня несколько параметров, а с ними var ud = updateD; не прокатывает..


    Лучше все таки присвоить локальным переменным, лямбда выражение так надежнее работает.
    Либо просто создайте объект NewDataEventArgs вне лямбды, а в лямбду передайте уже созданный.
    • Помечено в качестве ответа Qwester33 27 июля 2011 г. 20:15
    27 июля 2011 г. 19:42
    Отвечающий

Все ответы

  • protected virtual void OnNewDataEvent(List<D> updateD)
    {
       var handler = NewDataEvent;
    
       var ud = updateD;
       if (handler != null)
         ThreadPool.QueueUserWorkItem((st) => handler(this, new NewDataEventArgs(ud)));
    }
    
    
    

    • Помечено в качестве ответа Qwester33 27 июля 2011 г. 19:07
    27 июля 2011 г. 18:34
    Отвечающий
  • Круто.

    Вроде работает, даже если убрать:

     

     var ud = updateD;
     if (handler != null)
      ThreadPool.QueueUserWorkItem((st) => handler(this, new NewDataEventArgs(ud)));
    

    и сделать просто:

     

     if (handler != null)
      ThreadPool.QueueUserWorkItem((st) => handler(this, new NewDataEventArgs(updateD)));
    

     

    Так тоже можно? Я почему уточняю, потомоу что у меня несколько параметров, а с ними var ud = updateD; не прокатывает..

    27 июля 2011 г. 19:11
  • Круто.

    Вроде работает, даже если убрать:

     

     var ud = updateD;
     if (handler != null)
     ThreadPool.QueueUserWorkItem((st) => handler(this, new NewDataEventArgs(ud)));
    

    и сделать просто:

     

     if (handler != null)
     ThreadPool.QueueUserWorkItem((st) => handler(this, new NewDataEventArgs(updateD)));
    

     

    Так тоже можно? Я почему уточняю, потомоу что у меня несколько параметров, а с ними var ud = updateD; не прокатывает..


    Лучше все таки присвоить локальным переменным, лямбда выражение так надежнее работает.
    Либо просто создайте объект NewDataEventArgs вне лямбды, а в лямбду передайте уже созданный.
    • Помечено в качестве ответа Qwester33 27 июля 2011 г. 20:15
    27 июля 2011 г. 19:42
    Отвечающий
  • Пасибо.

    А я могу быть уверен, что все вызовы обработчиков событий будут обработаны последовательно? Или возможна ситуация, что была запущена обработка в потоке номер 3, эта обработка по какой-то причине замедлилась, и, до ее окончания, была запущена и выполнена следующая обработка в потоке номер 4? Точнее говоря, тут проблема даже не в столько в последовательности, сколько в том, что одновременный запуск обработок может (наверное) привести к ошибкам, так как получится разнопоточный доступ к одним и тем же сущностям, используемым внутри обработчика? Ведь обработчик запускается в новом потоке, но поток-то использует те же сущности (переменные уровня класса, содержащего метод-обработчик), с которыми работает предыдуще запущенный поток?

     

    27 июля 2011 г. 20:15
  • Вызов всех обработчиков выполнится строго последовательно, ведь все они работают в одном потоке.

    Другое дело, что пока обрабатывается событие, может возникнуть новый вызов OnNewDataEvent и создастся еще один поток, в котором теже обработчики будут обрабатывать новое событие.

    27 июля 2011 г. 20:54
    Отвечающий
  • А что делать, если обработчик события расположен в конкретном экземпляре, и до окончания обработки в методе этого экземпляра потоком 3, произошло новое поднятие события, и его выполнение запущено в потоке 4? Или это не проблема и все выполняется последовательно и потокобезопасно?

    27 июля 2011 г. 23:07
  • А что делать, если обработчик события расположен в конкретном экземпляре, и до окончания обработки в методе этого экземпляра потоком 3, произошло новое поднятие события, и его выполнение запущено в потоке 4? Или это не проблема и все выполняется последовательно и потокобезопасно?


    В таком случае два потока обработчиков событий будут выполняться паралельно. Потокобезопасность вы должны будете обеспечивать сами.

    28 июля 2011 г. 7:05
    Отвечающий
  • А эта проблема не имеет стандартных решений? Ведь при любом алгоритме возможны такие ситуации, что будет следующий вызов обработчика до окончания прошлого вызова обработчика?  Ведь есть, наверное, проверенные способы избежания таких раскладов средствами самих событий и потоков?


    Мне надо ее точно решать, так как при увеличении нагрузки (количество поднятий события), все стало падать...
    28 июля 2011 г. 8:14
  • Ну, вы можете просто синхронизировать следующим образом:

    object locker = new Object();
    protected virtual void OnNewDataEvent(List<D> updateD)
    {
      var handler = NewDataEvent;
    
      var ud = updateD;
    if (handler != null)
       ThreadPool.QueueUserWorkItem((st) => 
    	{
      lock(locker) handler(this, new NewDataEventArgs(ud));
    }
    );
    }
    
    

    В таком случае одновременно будет выполняться только один обработчик, а остальные будут ждать. Но это не очень хорошее решение.

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

    А вообще, когда события поступают чаще, чем их могут обработать обработчики - это ненормальная ситуация. Вы хотите взаимоисключающих вещей - вы хотите чтобы обработчки не тормозили основной поток, но при этом обрботчики не успевают отрабтать между двумя событиями.

    28 июля 2011 г. 19:38
    Отвечающий
  • 1. То есть, при таком решении, я получаю разгрузку потока, генерирующего события, но обработка всех событий будет идти по сути однопоточно? Это не совсем то, что мне нужно.

    2. В случае организации очереди, мне же все равно придется каким-то образом определять факт завершения прошлого вызова обработчика.

    То есть, Вы хотите сказать, что стандартного решения этой проблемы нет потому, что она и не должна возникать при правильной архитектуре приложения?

    29 июля 2011 г. 11:09
  • >1. То есть, при таком решении, я получаю разгрузку потока, генерирующего события, но обработка всех событий будет идти по сути однопоточно?

    Да

    >2. В случае организации очереди, мне же все равно придется каким-то образом определять факт завершения прошлого вызова обработчика.

    Не обязательно. Если очередь будет читаться одним потоком, то обработчики будут естественным образом обрабатьсяваться последовательно.

    >То есть, Вы хотите сказать, что стандартного решения этой проблемы нет потому, что она и не должна возникать при правильной архитектуре приложения?

    Я не могу понять ваши требования. С одной стороны вы хотите, что бы события обраатывались последовательно, с другой стороны - что бы они выполнялись паралельно в разных потоках. Так не бывает. Сформулируйте четко условия задачи, тогда можно будет говорить о стандартных решениях.

    29 июля 2011 г. 14:45
    Отвечающий