关于CountdownEvent网上的介绍比较少,因为它是实现和使用都很简单,先看看网上的一些评论吧:

CountDownEvent调用成员函数Wait()将阻塞,直至成员函数Signal() 被调用达特定的次数,这时CountDownEvent称作就绪态,对于处于就绪态的CountDownEvent,调用Wait()函数将不会再阻塞,只有手动调用Reset()函数后,调用Wait()函数将再次阻塞。CountDownEvent可以通过TryAddCount()和AddCount()函数来增加函数Signal() 需被调用的次数,但只有当CountDownEvent处于未就绪态时才会成功。否则根据调用函数的不同,将有可能抛出异常
当有新的需要同步的任务产生时,就调用AddCount增加它的计数,当有任务到达同步点是,就调用Signal函数减小它的计数,当CountdownEvent的计数为零时,就表示所有需要同步的任务已经完成,可以开始下一步任务了。

我们来看看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 < )
{
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 < ? : observedCount;
}
}
public bool IsSet
{
get
{
return (m_currentCount <= );
}
} //<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 <= )
{
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 < )
{
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 <= )
{
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 >= , "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 <= )
{
throw new ArgumentOutOfRangeException("signalCount");
} ThrowIfDisposed();
int observedCount;
SpinWait spin = new SpinWait();
while (true)
{
observedCount = m_currentCount;
if (observedCount <= )
{
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 < )
{
throw new ArgumentOutOfRangeException("count");
}
m_currentCount = count;
m_initialCount = count; if (count == )
{
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 < -)
{
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方法。

C# CountdownEvent实现的更多相关文章

  1. 【C#】【Thread】CountdownEvent任务并行

    System.Threading.CountdownEvent  是一个同步基元,它在收到一定次数的信号之后,将会解除对其等待线程的锁定. CountdownEvent  专门用于以下情况:您必须使用 ...

  2. 重新想象 Windows 8 Store Apps (47) - 多线程之线程同步: Semaphore, CountdownEvent, Barrier, ManualResetEvent, AutoResetEvent

    [源码下载] 重新想象 Windows 8 Store Apps (47) - 多线程之线程同步: Semaphore, CountdownEvent, Barrier, ManualResetEve ...

  3. 并行编程之CountdownEvent的用法

    教程:http://blog.gkarch.com/threading/part5.html#the-parallel-class http://www.cnblogs.com/huangxinche ...

  4. C#并行编程 (Barrier,CountdownEvent,ManualResetEventSlim,SemaphoreSlim,SpinLock,SpinWait )

    背景 有时候必须访问变量.实例.方法.属性或者结构体,而这些并没有准备好用于并发访问,或者有时候需要执行部分代码,而这些代码必须单独运行,这是不得不通过将任务分解的方式让它们独立运行. 当任务和线程要 ...

  5. [C#]『CountdownEvent』任务并行库使用小计

    System.Threading.CountdownEvent  是一个同步基元,它在收到一定次数的信号之后,将会解除对其等待线程的锁定. CountdownEvent  专门用于以下情况:您必须使用 ...

  6. 【Thread】CountdownEvent任务并行[z]

    System.Threading.CountdownEvent  是一个同步基元,它在收到一定次数的信号之后,将会解除对其等待线程的锁定. CountdownEvent  专门用于以下情况:您必须使用 ...

  7. 线程同步-使用CountDownEvent类

    CountDownEvent类:信号类,等待直到一定数量的操作完成. 代码Demo: using System; using System.Threading; Main方法下面加入以下代码片段: p ...

  8. [.net 多线程]CountdownEvent

    System.Threading.CountdownEvent 是一个同步基元,它在收到一定次数的信号之后,将会解除对其等待线程的锁定.CountdownEvent在初始化时有一个初始计数量,在每个工 ...

  9. 《C#多线程编程实战》2.7 CountDownEvent

    这个同步线程的类大概是东北的. 很有意思. 会限定你的线程使用的次数,更形象一点的像是你妈,提前准备好你要使用的线程的次数,用不完还不高兴那种的. 使用顺序基本就是 实例化  填充线程的启动次数 使用 ...

随机推荐

  1. 关于mac的一些常用操作记录

    之前记录过一个关于mac远程连接window机,实现共享文件的记录,今天记录一些常用的操作,会持续更新. 1.谷歌浏览器 f12的操作 command+option+i 打开调试面板 2.打开指定位置 ...

  2. Mysql 锁库与锁表

    一.全局锁表 1.FLUSH TABLES WITH READ LOCK 这个命令是全局读锁定,执行了命令之后所有库所有表都被锁定只读.一般都是用在数据库联机备份,这个时候数据库的写操作将被阻塞,读操 ...

  3. bootstrap AdminLTE模版的使用

    参考:  https://adminlte.io/themes/AdminLTE/documentation/index.html  https://blog.csdn.net/gebitan505/ ...

  4. kmp基础 ekmp

    +]; int lenp,lens; +];//可以是char 也可以是string +]; void getnext() { nex[]=-; ,j=; ) { ||p[j]==p[k]) nex[ ...

  5. 2018-03-11 20165235 祁瑛 Java第二周考试总结

    20165235 祁瑛 Java第二周考试总结 课后习题p16 代码编写 class Person { void speakHello (){ System.out.print("nin h ...

  6. Spring事务的传播:PROPAGATION_REQUIRED

    PROPAGATION_REQUIRED-- 支持当前事务,如果当前没有事务,就新建一个事务.这是最常见的选择. ServiceA { void methodA() { ServiceB.method ...

  7. oracle连接连表查询时,两表的连接字段类型不一致的时候,会导致ora 01722无效数字错误,这时候需要转换

    类型不匹配,需要类型转换,函数:to_char()转换成字符,to_num()转换成数字

  8. Idea中lombok不生效原因

    我们可以通过在maven中插入配置信息 <dependency> <groupId>org.projectlombok</groupId> <artifact ...

  9. Shell学习之Bash变量详解(二)

    Shell学习之Bash变量详解 目录 Bash变量 Bash变量注意点 用户自定义变量 环境变量 位置参数变量 预定义变量 Bash变量 用户自定义变量:在Bash中由用户定义的变量. 环境变量:这 ...

  10. CSS-样式篇

    2文本: 1文本缩进:text-indent:理论上只有块级元素才可以设置(百分比是相对于父元素的宽度,负值的话要设置内边距进行抵消,防止超过浏览器边界),行内元素可以通过内边距和外边距来达到同样的效 ...