關於CountdownEvent網上的介紹比較少,由於它是實現和使用都很簡單,先看看網上的一些評論吧:函數
CountDownEvent調用成員函數Wait()將阻塞,直至成員函數Signal() 被調用達特定的次數,這時CountDownEvent稱做就緒態,對於處於就緒態的CountDownEvent,調用Wait()函數將不會再阻塞,只有手動調用Reset()函數後,調用Wait()函數將再次阻塞。CountDownEvent能夠經過TryAddCount()和AddCount()函數來增長函數Signal() 需被調用的次數,但只有當CountDownEvent處於未就緒態時纔會成功。不然根據調用函數的不一樣,將有可能拋出異常
當有新的須要同步的任務產生時,就調用AddCount增長它的計數,當有任務到達同步點是,就調用Signal函數減少它的計數,當CountdownEvent的計數爲零時,就表示全部須要同步的任務已經完成,能夠開始下一步任務了。spa
咱們來看看CountdownEvent的實現:線程
public class CountdownEvent : IDisposable { private int m_initialCount; // The original # of signals the latch was instantiated with. private volatile int m_currentCount; // The # of outstanding signals before the latch transitions to a signaled state. private ManualResetEventSlim m_event; // An event used to manage blocking and signaling. private volatile bool m_disposed; // Whether the latch has been disposed. public CountdownEvent(int initialCount) { if (initialCount < 0) { throw new ArgumentOutOfRangeException("initialCount"); } m_initialCount = initialCount; m_currentCount = initialCount; // Allocate a thin event, which internally defers creation of an actual Win32 event. m_event = new ManualResetEventSlim(); // If the latch was created with a count of 0, then it's already in the signaled state. if (initialCount == 0) { m_event.Set(); } } public int CurrentCount { get { int observedCount = m_currentCount; return observedCount < 0 ? 0 : observedCount; } } public bool IsSet { get { return (m_currentCount <= 0); } } //<returns>true if the signal caused the count to reach zero and the event was set; otherwise, false. public bool Signal() { ThrowIfDisposed(); Contract.Assert(m_event != null); if (m_currentCount <= 0) { throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Decrement_BelowZero")); } int newCount = Interlocked.Decrement(ref m_currentCount); if (newCount == 0) { m_event.Set(); return true; } else if (newCount < 0) { throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Decrement_BelowZero")); } return false; } //<returns>true if the signals caused the count to reach zero and the event was set; otherwise, false. public bool Signal(int signalCount) { if (signalCount <= 0) { throw new ArgumentOutOfRangeException("signalCount"); } ThrowIfDisposed(); Contract.Assert(m_event != null); int observedCount; SpinWait spin = new SpinWait(); while (true) { observedCount = m_currentCount; if (observedCount < signalCount) { throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Decrement_BelowZero")); } if (Interlocked.CompareExchange(ref m_currentCount, observedCount - signalCount, observedCount) == observedCount) { break; } spin.SpinOnce(); } if (observedCount == signalCount) { m_event.Set(); return true; } Contract.Assert(m_currentCount >= 0, "latch was decremented below zero"); return false; } public void AddCount(int signalCount) { if (!TryAddCount(signalCount)) { throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Increment_AlreadyZero")); } } //<returns> true if the increment succeeded; otherwise, false public bool TryAddCount(int signalCount) { if (signalCount <= 0) { throw new ArgumentOutOfRangeException("signalCount"); } ThrowIfDisposed(); int observedCount; SpinWait spin = new SpinWait(); while (true) { observedCount = m_currentCount; if (observedCount <= 0) { return false; } else if (observedCount > (Int32.MaxValue - signalCount)) { throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Increment_AlreadyMax")); } if (Interlocked.CompareExchange(ref m_currentCount, observedCount + signalCount, observedCount) == observedCount) { break; } spin.SpinOnce(); } return true; } public void Reset() { Reset(m_initialCount); } public void Reset(int count) { ThrowIfDisposed(); if (count < 0) { throw new ArgumentOutOfRangeException("count"); } m_currentCount = count; m_initialCount = count; if (count == 0) { m_event.Set(); } else { m_event.Reset(); } } //Blocks the current thread until the is set public void Wait() { Wait(Timeout.Infinite, new CancellationToken()); } public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException("millisecondsTimeout"); } ThrowIfDisposed(); cancellationToken.ThrowIfCancellationRequested(); bool returnValue = IsSet; // If not completed yet, wait on the event. if (!returnValue) { // ** the actual wait returnValue = m_event.Wait(millisecondsTimeout, cancellationToken); //the Wait will throw OCE itself if the token is canceled. } return returnValue; } }
代碼很是簡單,CountdownEvent內部是實現仍是依賴於ManualResetEventSlim實例,int initialCount參數爲0是就調用ManualResetEventSlim的set方法,這樣Wait的對象就不被阻塞了。IsSet是一個主要屬性【return (m_currentCount <= 0)】,Signal()和Signal(int signalCount)方法就是減小m_currentCount的,主要採用原子操做【Interlocked.Decrement(ref m_currentCount)】 和【Interlocked.CompareExchange(ref m_currentCount, observedCount - signalCount, observedCount) == observedCount】,若是減小後m_currentCount==0 就調用set方法,爲Wait的線程放行;注意這裏面有使用SpinWait的自旋,那麼AddCount、TryAddCount和Signal方法相反,主要是增長m_currentCount,實現方式同樣,採用原子操做+自旋;Reset還原爲初始值,Reset(int count)還原爲制定的值,Wait方法主要看是不是IsSet【是則是調用了Set方法的】,則直接返回【當前m_currentCount==0】,否者就調用ManualResetEventSlim的Wai方法。code