C# CountdownEvent实现
关于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实现的更多相关文章
- 【C#】【Thread】CountdownEvent任务并行
System.Threading.CountdownEvent 是一个同步基元,它在收到一定次数的信号之后,将会解除对其等待线程的锁定. CountdownEvent 专门用于以下情况:您必须使用 ...
- 重新想象 Windows 8 Store Apps (47) - 多线程之线程同步: Semaphore, CountdownEvent, Barrier, ManualResetEvent, AutoResetEvent
[源码下载] 重新想象 Windows 8 Store Apps (47) - 多线程之线程同步: Semaphore, CountdownEvent, Barrier, ManualResetEve ...
- 并行编程之CountdownEvent的用法
教程:http://blog.gkarch.com/threading/part5.html#the-parallel-class http://www.cnblogs.com/huangxinche ...
- C#并行编程 (Barrier,CountdownEvent,ManualResetEventSlim,SemaphoreSlim,SpinLock,SpinWait )
背景 有时候必须访问变量.实例.方法.属性或者结构体,而这些并没有准备好用于并发访问,或者有时候需要执行部分代码,而这些代码必须单独运行,这是不得不通过将任务分解的方式让它们独立运行. 当任务和线程要 ...
- [C#]『CountdownEvent』任务并行库使用小计
System.Threading.CountdownEvent 是一个同步基元,它在收到一定次数的信号之后,将会解除对其等待线程的锁定. CountdownEvent 专门用于以下情况:您必须使用 ...
- 【Thread】CountdownEvent任务并行[z]
System.Threading.CountdownEvent 是一个同步基元,它在收到一定次数的信号之后,将会解除对其等待线程的锁定. CountdownEvent 专门用于以下情况:您必须使用 ...
- 线程同步-使用CountDownEvent类
CountDownEvent类:信号类,等待直到一定数量的操作完成. 代码Demo: using System; using System.Threading; Main方法下面加入以下代码片段: p ...
- [.net 多线程]CountdownEvent
System.Threading.CountdownEvent 是一个同步基元,它在收到一定次数的信号之后,将会解除对其等待线程的锁定.CountdownEvent在初始化时有一个初始计数量,在每个工 ...
- 《C#多线程编程实战》2.7 CountDownEvent
这个同步线程的类大概是东北的. 很有意思. 会限定你的线程使用的次数,更形象一点的像是你妈,提前准备好你要使用的线程的次数,用不完还不高兴那种的. 使用顺序基本就是 实例化 填充线程的启动次数 使用 ...
随机推荐
- BZOJ3262/洛谷P3810 陌上花开 分治 三维偏序 树状数组
原文链接http://www.cnblogs.com/zhouzhendong/p/8672131.html 题目传送门 - BZOJ3262 题目传送门 - 洛谷P3810 题意 有$n$个元素,第 ...
- waf python build 工具使用流程
waf python build 工具使用流程 waf 的 build 理念 build 了之后,可以跟踪到 ${SRC} 和 ${TGT} 有关联的文件,只有 ${SRC} 被修改过,在下次buil ...
- html-列表-3
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- python-飞机大战
效果图 main.py import time import pygame from EnemyPlane import EnemyPlane from HeroPlane import HeroPl ...
- git命令详解( 七 )
此为git命令详解的第七篇 这章我们可以来逐渐揭开 git push.fetch 和 pull 的神秘面纱了.我们会逐个介绍这几个命令,它们在理念上是非常相似的. git push的参数 git ...
- Java网络编程案例---聊天室
网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来. java.net包中JavaSE的API包含有类和接口,它们提供低层次的通信细节.你可以直接使用这些类和接口,来专注于解决 ...
- SpringMVC(十一) RequestMapping获取Cookie值
可以在控制器方法中使用类似@CookieValue("JSESSIONID") String sessionID的方式,来获取请求中的Cookie的值. 样例控制器代码 packa ...
- BZOJ.3631.[JLOI2014]松鼠的新家(树上差分)
题目链接 树剖/差分裸题.. //28260kb 584ms #include <cstdio> #include <cctype> #include <algorith ...
- 2017-9-24-Linux移植:ubuntu server 16.04无法联网&无法apt-get update解决
无法上网!!!不能忍.. 现象:ifconfig 毛都没有,想找一下ip都找不到. ifconfig –a 可以列出所有网卡设备,确认VM VirtualBox网卡开对了,已经给到了虚拟机. 编辑/e ...
- Python一行代码处理地理围栏
最近在工作中遇到了这个一个需求,用户设定地理围栏,后台获取到实时位置信息后通过与围栏比较,判断是否越界等. 这个过程需要用到数据协议为GEOjson,通过查阅资料后,发现python的shapely库 ...