2014-12-12 4 views
0

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

Вызов метода Set() должен освободить все ожидания темы, а не только один.

Как я могу построить такой класс? Я просто из идей?

  • Martin.
+0

[Monitor.Pulse] (http://msdn.microsoft.com/en-us/library/system.threading.monitor (v = vs.110) .aspx)? –

+0

@LB: Я думаю, вы имеете в виду ['Monitor.PulseAll()'] (http://msdn.microsoft.com/en-us/library/system.threading.monitor.pulseall (v = vs.110) .aspx) –

+0

Monitor.PulseAll() требует, чтобы у вас была блокировка объекта. У AutoResetEvent нет таких требований для своего соответствующего метода Set(). Я не могу себе представить, как построить мой класс в конструкции монитора ??? –

ответ

0

Огромное спасибо всем вашим тысячам и входам, которые я прочитал с большим интересом. Я сделал еще несколько поисков здесь, на Stackoverflow, и неожиданно обнаружил this, который оказался именно тем, что я искал. Разрезав его вниз только двумя способами, мне нужно, я закончил с этой небольшой класс:

public sealed class Signaller 
{ 
    public void PulseAll() 
    { 
     lock (_lock) 
     { 
      Monitor.PulseAll(_lock); 
     } 
    } 

    public bool Wait(TimeSpan maxWaitTime) 
    { 
     lock (_lock) 
     { 
      return Monitor.Wait(_lock, maxWaitTime); 
     } 
    } 

    private readonly object _lock = new object(); 
} 

и это делает excactly то, что надо! Я поражен тем, что решение может быть таким простым, и я люблю такую ​​простоту. Оно прекрасно. Спасибо, Мэтью Уотсон!

  • Martin.
1

Итак, у вас есть несколько потоков, выполняющих .WaitOne(), и вы хотите их освободить?

Используйте класс ManualResetEvent и все ожидающие потоки должны освободить ...

+0

Я уже пробовал это, но у меня возникают следующие проблемы: 1) вызов Set() оставляет ворота открытыми, позволяя другим потокам, которые вызывают WaitOne(), пройти, пока вы не вызовете вручную Reset(), который закроет ворота , Вы не можете гарантировать, что только потоки, которые ожидали, пройдут, пока они открыты. 2) Если вы вызываете Set(), а затем сразу же после сброса вызова(), ни один из ожидающих потоков не будет отпущен. После вызова Set() потоки заблокированных потоков начинают течь, но не все сразу. Я попробовал Thread.Sleep (10) между Set() и Reset(), и только 8 из 20 заблокированных потоков были пропущены. –

+3

@Martin без дополнительной синхронизации нет заметной разницы между потоком, прибывающим перед вызовом set или afterwards. То, что вам кажется нужным, - это атомная версия всех функций нитей - не совсем тривиальная, но выполнимая (монитор и int - это все, что вам нужно, afaics). Большая проблема заключается в том, что я не могу придумать ни одной ситуации, когда необходимость такой функциональности не является признаком большего недостатка дизайна. Вы должны описать то, что вы на самом деле пытаетесь достичь, может быть лучший способ достичь этого. – Voo

+0

^^ какой @Voo сказал. –

0

Две вещи, которые вы могли бы попробовать.

Использование объекта «Барьер» добавляет условное добавление потоков тоже и сигнализирует об этом.

Другой может быть использование настройки подписчика издателя, как в RX. Каждый поток ожидает объект, который он передает в коллекцию. Когда вы хотите вызвать цикл «set» над моментальным снимком его вызова, установленного для каждого члена.

Или вы можете попробовать медведи.

+0

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

0

Если событие ссылается на все потоки в общем поле или свойстве, вы можете заменить общее поле или свойство новым несигнальным событием, а затем передать старый. У него есть некоторые затраты на него, поскольку вы будете регулярно создавать новые объекты синхронизации, но это сработает. Вот пример того, как я хотел бы сделать это:

public static class Example 
{ 
    private static volatile bool stopRunning; 
    private static ReleasingAutoResetEvent myEvent; 

    public static void RunExample() 
    { 
     using (Example.myEvent = new ReleasingAutoResetEvent()) 
     { 
      WaitCallback work = new WaitCallback(WaitThread); 

      for (int i = 0; i < 5; ++i) 
      { 
       ThreadPool.QueueUserWorkItem(work, i.ToString()); 
      } 

      Thread.Sleep(500); 

      for (int i = 0; i < 3; ++i) 
      { 
       Example.myEvent.Set(); 
       Thread.Sleep(5000); 
      } 

      Example.stopRunning = true; 
      Example.myEvent.Set(); 
     } 
    } 

    private static void WaitThread(object state) 
    { 
     while (!Example.stopRunning) 
     { 
      Example.myEvent.WaitOne(); 
      Console.WriteLine("Thread {0} is released!", state); 
     } 
    } 
} 

public sealed class ReleasingAutoResetEvent : IDisposable 
{ 
    private volatile ManualResetEvent manualResetEvent = new ManualResetEvent(false); 

    public void Set() 
    { 
     ManualResetEvent eventToSet = this.manualResetEvent; 
     this.manualResetEvent = new ManualResetEvent(false); 
     eventToSet.Set(); 
     eventToSet.Dispose(); 
    } 

    public bool WaitOne() 
    { 
     return this.manualResetEvent.WaitOne(); 
    } 

    public bool WaitOne(int millisecondsTimeout) 
    { 
     return this.manualResetEvent.WaitOne(millisecondsTimeout); 
    } 

    public bool WaitOne(TimeSpan timeout) 
    { 
     return this.manualResetEvent.WaitOne(timeout); 
    } 

    public void Dispose() 
    { 
     this.manualResetEvent.Dispose(); 
    } 
} 
0

Еще более легкое решение, вы можете попробовать, который использует класс Monitor для блокировки и разблокировки объектов ниже. Тем не менее, я не так доволен рассказом об очистке этой версии ReleaseasingAutoResetEvent, так как на мониторе может содержаться ссылка на нее и поддерживать ее на неопределенное время, если она не настроена должным образом.

Есть несколько ограничений/gotchas с этой реализацией. Во-первых, поток, который создает этот объект, будет единственным, который сможет сигнализировать об этом вызовом Set; другие потоки, которые пытаются сделать то же самое, получат исключение SynchronizationLockException. Во-вторых, поток, создавший его, никогда не сможет ждать его успешно, так как он уже владеет блокировкой. Это будет эффективным решением, если у вас есть только один управляющий поток и несколько других ожидающих потоков.

public static class Example 
{ 
    private static volatile bool stopRunning; 
    private static ReleasingAutoResetEvent myEvent; 

    public static void RunExample() 
    { 
     using (Example.myEvent = new ReleasingAutoResetEvent()) 
     { 
      WaitCallback work = new WaitCallback(WaitThread); 

      for (int i = 0; i < 5; ++i) 
      { 
       ThreadPool.QueueUserWorkItem(work, i.ToString()); 
      } 

      Thread.Sleep(500); 

      for (int i = 0; i < 3; ++i) 
      { 
       Example.myEvent.Set(); 
       Thread.Sleep(5000); 
      } 

      Example.stopRunning = true; 
      Example.myEvent.Set(); 
     } 
    } 

    private static void WaitThread(object state) 
    { 
     while (!Example.stopRunning) 
     { 
      Example.myEvent.WaitOne(); 
      Console.WriteLine("Thread {0} is released!", state); 
     } 
    } 
} 

public sealed class ReleasingAutoResetEvent : IDisposable 
{ 
    private volatile object lockObject = new object(); 

    public ReleasingAutoResetEvent() 
    { 
     Monitor.Enter(this.lockObject); 
    } 

    public void Set() 
    { 
     object objectToSignal = this.lockObject; 
     object objectToLock = new object(); 

     Monitor.Enter(objectToLock); 
     this.lockObject = objectToLock; 
     Monitor.Exit(objectToSignal); 
    } 

    public void WaitOne() 
    { 
     object objectToMonitor = this.lockObject; 
     Monitor.Enter(objectToMonitor); 
     Monitor.Exit(objectToMonitor); 
    } 

    public bool WaitOne(int millisecondsTimeout) 
    { 
     object objectToMonitor = this.lockObject; 
     bool succeeded = Monitor.TryEnter(objectToMonitor, millisecondsTimeout); 

     if (succeeded) 
     { 
      Monitor.Exit(objectToMonitor); 
     } 

     return succeeded; 
    } 

    public bool WaitOne(TimeSpan timeout) 
    { 
     object objectToMonitor = this.lockObject; 
     bool succeeded = Monitor.TryEnter(objectToMonitor, timeout); 

     if (succeeded) 
     { 
      Monitor.Exit(objectToMonitor); 
     } 

     return succeeded; 
    } 

    public void Dispose() 
    { 
     Monitor.Exit(this.lockObject); 
    } 
} 
+0

Упомянутый ответ на соответствующие ответы выглядит как ответ на вопрос [AutoResetEvent Reset сразу после Set] (http://stackoverflow.com/questions/18609285/autoresetevent-reset-immediately-after- set? rq = 1) лучше, чем у меня для использования Monitor для достижения вашей цели. Для вашего сценария вы бы хотели вызвать метод PulseAll, который предлагает освободить все текущие ожидающие потоки. –