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
这个同步线程的类大概是东北的. 很有意思. 会限定你的线程使用的次数,更形象一点的像是你妈,提前准备好你要使用的线程的次数,用不完还不高兴那种的. 使用顺序基本就是 实例化 填充线程的启动次数 使用 ...
随机推荐
- net core体系-web应用程序-4net core2.0大白话带你入门-7asp.net core日志组件(Logger和Nlog)
asp.net core日志组件 日志介绍 Logging的使用 1. appsettings.json中Logging的介绍 Logging的配置信息是保存在appsettings.json配置 ...
- CSS改变插入光标颜色caret-color
CSS代码: input { color: #333; caret-color: red; } @supports (-webkit-mask: none) and (not (caret-color ...
- 洛谷3825 [NOI2017]游戏 2-sat
原文链接http://www.cnblogs.com/zhouzhendong/p/8146041.html 题目传送门 - 洛谷3825 题解 我们考虑到地图中x的个数很少,最多只有8个. 所以我们 ...
- There is no setter for property named 可能产生的原因!
There is no setter for property named 'operateIP ' in 'class com.chinaunicom.wsp.facade.entity.User ...
- sql union 列的字段不一样的时候
- cmd使用notepad++为打开方式打开文件
想放一个txt进入vstart中,但是又不想用系统自带的记事本打开,想在vstart中双击时使用notepad++打开. cmd命令如下: "D:\notepad++\notepad++.e ...
- 002.Zabbix简介
一 Zabbix简介 1.1 概述 Zabbix是一个企业级的高度集成开源监控软件,提供分布式监控解决方案.可以用来监控设备.服务等可用性和性能. 1.2 所支持监控方式 目前由zabbix提供包括但 ...
- c++ <vector>学习
https://www.cnblogs.com/mr-wid/archive/2013/01/22/2871105.html 具体参考上面博客,很详细,也是看他的.今天c++学习200%,项目开发0% ...
- Redis基础、应用、第三方支持组件总结
这段时间一直在研究学习Redis的相关知识,现在大概做下总结吧首先,Redis的基础方面,不说配置,就单单说下Redis数据类型:Redis 五大数据类型有String 类型,Hash 类型,List ...
- 【C#】Convert.ToInt32、(int)和int.Parse三者的区别
前者适合将object类类型转换成int类型 (int)适合简单数据类型之间的转换: int.Parse适合将string类类型转换成int类型.