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
这个同步线程的类大概是东北的. 很有意思. 会限定你的线程使用的次数,更形象一点的像是你妈,提前准备好你要使用的线程的次数,用不完还不高兴那种的. 使用顺序基本就是 实例化 填充线程的启动次数 使用 ...
随机推荐
- css3 webkit-box的用法
webkit-box 1.之前要实现横列的web布局,通常就是float或者display:inline-block; 但是都不能做到真正的流体布局.至少width要自己去算百分比.2.flexibl ...
- 如何让自己的Dev C++用上C++11,c++14标准
首先确保Dev C++版本是最新的5.11版 其实用C++11和C++14标准的语法去运行还是会出现结果的,最多warning一下 但完美主义者是不允许这样的 我们可以点击菜单栏的“工具”-> ...
- Sublime text 3搭建Python开发环境及常用插件安装
参考 https://www.cnblogs.com/honkly/p/6599642.html 一.环境准备 1.官方网站地址 2.Windows 10 3.Sublime Text 3 + 官网 ...
- CLR Via 第一 章 知识点整理(1)
写这个纯粹是自己的一点学习总结,其实就学习的笔记整理,相当于对自己的一点督促,如有看到不正确的欢迎指出 一般我们写代码都是使用的高级语言,但是在CLR中运行的代码并不是我们直接写的代码,而是通过我们选 ...
- CISCO ACL配置(目前)
什么是ACL? 访问控制列表简称为ACL,访问控制列表使用包过滤技术,在路由器上读取第三层及第四层包头中的信息如源地址,目的地址,源端口,目的端口等,根据预先定义好的规则对包进行过滤,从而达到访问控制 ...
- HashMap 源码阅读
前言 之前读过一些类的源码,近来发现都忘了,再读一遍整理记录一下.这次读的是 JDK 11 的代码,贴上来的源码会去掉大部分的注释, 也会加上一些自己的理解. Map 接口 这里提一下 Map 接口与 ...
- SQL Server中的标识列
一.标识列的定义以及特点 SQL Server中的标识列又称标识符列,习惯上又叫自增列. 该种列具有以下三种特点: .列的数据类型为不带小数的数值类型 .在进行插入(Insert)操作时,该列的值是由 ...
- Codeforces Round #541 (Div. 2) (A~F)
目录 Codeforces 1131 A.Sea Battle B.Draw! C.Birthday D.Gourmet choice(拓扑排序) E.String Multiplication(思路 ...
- Navicat破解
Navicat是一款我们经常使用的可视化sql工具,可偏偏它呢又收费,本着浪费可耻的理念,只有去网上找各种破解方法,却发现很多方法对于Navicat 12都不支持(本人使用的Navicat 12).最 ...
- Java并发编程(七)-- ThreadLocal
提到ThreadLocal,有些Android或者Java程序员可能有所陌生,可能会提出种种问题,它是做什么的,是不是和线程有关,怎么使用呢?等等问题,本文将总结一下我对ThreadLocal的理解和 ...