Облегченная альтернатива Manual / AutoResetEvent в C # - PullRequest
5 голосов
/ 12 мая 2010

Я написал, как я надеюсь, легкую альтернативу использованию классов ManualResetEvent и AutoResetEvent в C # /. NET. Причиной этого было использование функциональности, подобной Event, без необходимости использования объекта блокировки ядра.

Хотя кажется, что код хорошо работает как в тестировании, так и в производстве, правильно подобрать такую ​​штуку для всех возможностей может быть чреватым трудом, и я смиренно прошу любые конструктивные комментарии и критику со стороны группы StackOverflow по этому поводу. Надеюсь (после обзора) это будет полезно другим.

Использование должно быть аналогично классам Manual / AutoResetEvent с Notify (), используемым для Set ().

Вот так:

using System;
using System.Threading;

public class Signal
{
  private readonly object _lock = new object();
  private readonly bool _autoResetSignal;
  private bool _notified;

  public Signal()
    : this(false, false)
  {
  }

  public Signal(bool initialState, bool autoReset)
  {
    _autoResetSignal = autoReset;
    _notified = initialState;
  }

  public virtual void Notify()
  {
    lock (_lock)
    {
      // first time?
      if (!_notified)
      {
        // set the flag
        _notified = true;

        // unblock a thread which is waiting on this signal 
        Monitor.Pulse(_lock);
      }
    }
  }

  public void Wait()
  {
    Wait(Timeout.Infinite);
  }

  public virtual bool Wait(int milliseconds)
  {
    lock (_lock)
    {
      bool ret = true;
      // this check needs to be inside the lock otherwise you can get nailed
      // with a race condition where the notify thread sets the flag AFTER 
      // the waiting thread has checked it and acquires the lock and does the 
      // pulse before the Monitor.Wait below - when this happens the caller
      // will wait forever as he "just missed" the only pulse which is ever 
      // going to happen 
      if (!_notified)
      {
        ret = Monitor.Wait(_lock, milliseconds);
      }

      if (_autoResetSignal)
      {
        _notified = false;
      }
      return (ret);
    }
  }
}

Ответы [ 4 ]

4 голосов
/ 12 мая 2010

Это работает из предположения, что события Win32 стоят дорого. Это не так, мало что я могу придумать, это дешевле, чем мероприятие. Главный намек на то, что это так, - дизайнеры .NET решили, что было бы неплохо использовать событие Win32 для реализации MRE и ARE.

Истинная стоимость вашей замены - это основной FUD, который вы получите, если у вас есть многопоточность и вы не знаете, что ее вызывает.

1 голос
/ 04 ноября 2010

Один из способов оптимизировать производительность AutoResetEvent - сохранить его состояние (сигнальное / не сигнализированное) в вашей собственной переменной, поэтому перед тем, как совершить поездку в ядро ​​и фактически использовать объект события, вы можете просто проверить состояние вашей аппликативной переменной. и все время оставаться в режиме пользователя.
Я опубликовал демонстрацию этой концепции пару месяцев назад.

1 голос
/ 12 мая 2010

К сожалению, правильная реализация Monitor довольно тяжелая, учитывая примитивы синхронизации Win32. Мое первоначальное подозрение заключается в том, что «блокировка» будет более тяжелой при использовании ресурсов, чем событие (и, вероятно, будет построена поверх события).

0 голосов
/ 08 апреля 2013

Обычно любая синхронизация медленная, поэтому лучше их избегать.

Однако между их скоростью существует большая разница:

Поддерживаемый процессор Interlocked Exchange - самый быстрый, в то время как простой логический флаг все еще превосходит AutoresetEvent.

Проверьте это для полных примеров кода и сравнений производительности на AutoResetEvent и альтернативах.

...