关于c#多线程中的几个信号量
信号量在c#多线程通信中主要用来向阻塞的线程传达信号从而使得阻塞线程继续执行
多线程信号(线程交互):通常是指线程必须等待一个线程或者多个线程通知交互(释放信号)才可以继续执行
在c#中信号量主要有这几个 AutoResetEvent,ManualResetEvent,CountdownEvent,EventWaitHandle,Semaphore
AutoResetEvent
AutoResetEvent 在释放信号量后,会默认设置为无信号状态。AutoResetEvent 构造函数会传递一个initialState boolean 类型的参数,参数为false 时 需要主动去传递信
号量,传递信号量之后将重新设置为无信号状态。参数为ture 时会自动设置为有信号状态(终止状态),大体意思就是,会默认执行阻塞线程,不需要阻塞线程收到信号量才会执
行(不会阻塞调用线程)。在参数为ture 时,AutoResetEvent 类实例调用 Reset () 方法后,会将当前AutoResetEvent 类实例设置为无信号状态也就是 变成了一个 参数为
false 的 AutoResetEvent 类实例,在此之后的执行阻塞线程都需要主动去释放(传递)信号。
private static AutoResetEvent auto = new AutoResetEvent(false);
private static AutoResetEvent auto = new AutoResetEvent(ture);//有信号终止状态
Thread thread1 = new Thread(AutoResetEventHandler);
Console.WriteLine("当前线程id"+Thread.CurrentThread.ManagedThreadId);
thread1.Start();
Thread.Sleep(5000);
auto.Set();
// auto.Reset(); 在这种情况下new AutoResetEvent(ture) 的 类实例 会变成无信号未终止状态的 如果阻塞线程没有接收到信号量将会一直阻塞下去,直到接收到信号量
Thread thread2 = new Thread(AutoResetEventHandlerTwo);
thread2.Start();
Thread.Sleep(3000);//等待3秒
private static void AutoResetEventHandler()
{
Console.WriteLine("当前线程id" + Thread.CurrentThread.ManagedThreadId);
auto.WaitOne();//阻塞线程
Console.WriteLine("等待一秒后执行");
}
private static void AutoResetEventHandlerTwo()
{
auto.WaitOne();//阻塞线程
Console.WriteLine("我是第二个等待执行");
}
ManualResetEvent
ManualResetEvent 与上面的AutoResetEvent 类似在构造函数中也会传入一个Boolean类型参数,不同的是信号量的释放,AutoResetEvent在信号量释放后会自动设置为无信号状态(未终止状态),ManualResetEvent 需要我们手动调用Reset()方法将其设置为无信号量状态(未终止状态),否则其会一直保持有信号量状态(终止状态)ManualResetEvent 如果不手动重置信号量状态,阻塞线程将不会起作用,会立即执行。
private static ManualResetEvent manualReset = new ManualResetEvent(false);
private static ManualResetEvent manualReset = new ManualResetEvent(true);
Thread thread1 = new Thread(() => {
manualReset.WaitOne();
Console.WriteLine("最开始的执行");
});
thread1.Start();
Thread.Sleep(3000);//休眠--等待三秒
manualReset.Set();//释放信号量
Thread thread2 = new Thread(ManualResetEventHandler1);
thread2.Start();
manualReset.Reset();//充值信号量
Thread thread3=new Thread(ManualResetEventHandler2);
manualReset.Set();//释放信号量
thread3.Start();
manualReset.Reset();
private static void ManualResetEventHandler1()
{
manualReset.WaitOne();
Console.WriteLine("第一次等待执行");
}
private static void ManualResetEventHandler2()
{
manualReset.WaitOne();
Console.WriteLine("第二次等待执行");
}
上面说到过,ManualResetEvent 构造函数与AutoResetEvent构造函数是一样,通过bool类型的参数判读 类的实例是否默认释放信号量,不同的是ManualResetEvent 需要手动调用Reset()方法。上面代码中,我们传递了一个false参数,调用了Set()方法释放信号量,然后再调用Reset()方法重置信号量,如此反复一次,ManualResetEventHandler2 会一直阻塞 直到我们释放信号量,才会继续执行。
CountdownEvent
CountdownEvent 实例化是需要传入一个int 类型作为InitialCount初始值,CountdownEvent信号量的释放很特别,只有当Countdown类的实例的CurrentCount等于0时才会释放我们的信号量,Signal()方法每次调用都会使得CurrentCount进行-1操作。Reset()方法会重置为实例化对象时传递的参数值,也可以Reset(100)对我们的InitialCount重新赋值。
private static CountdownEvent countdownEvent = new CountdownEvent(1000);
CountReduce();
// countdownThread.Start();
Thread thread = new Thread(() => {
countdownEvent.Wait();
Console.WriteLine("直到CountdownEvent总数="+countdownEvent.CurrentCount+"我才执行");
//CountdownEvent.CurrentCount//当前总数
//CountdownEvent.AddCount()//添加1
//CountdownEvent.AddCount(10);//添加指定数量
//CountdownEvent.InitialCount//总数
//CountdownEvent.Reset()//设置为InitialCount初始值
//CountdownEvent.Reset(100)//设置为指定初始值
});
thread.Start();
private static async Task CountReduce()
{
await Task.Run(async () => {
for(var i = 0; i < 1000; i++)
{
await Task.Delay(100);//休眠100毫秒--等到100毫秒
//if (countdownEvent.CurrentCount < 10)
//{
// countdownEvent.Reset(100);
// CountReduce();
//}
countdownEvent.Signal();
Console.WriteLine("当前总数"+countdownEvent.CurrentCount);
}
});
}
上面代码中我们有用到异步方法但没有等待结果,但是但是线程的委托方法中调用了 countdownEvent.Wait()来阻塞线程;,只有当我们的CurrentCount等于0时才会释放信号量线程才不会阻塞得以继续执行(有感兴趣的可以试试这部分的代码)
EventWaitHandle
本地事件等待句柄是指创建EventWaitHandle 对象时指定EventResetMode枚举,可分为自动重置的事件等待句柄和手动重置的事件等待句柄。
关于事件等待句柄,不涉及到.NET 事件以及委托和事件处理程序,我们可以看一下官方的声明。

EvenetResetMode.AutoReset
看到AutoReset是不是想起了,我们上面的AutoResetEvent,其用法是一样的。在创建EventWaitHanlde对象时来指定是否自动重置信号状态。此同步事件表示一个等待线程(阻塞线程)在收到信号时自动重置信号状态。此事件向等待线程发送信号时,需要调用Set()方法
EvenetResetMode.ManualReset
在创建EventWaitHandle对象时指定手动重置信号状态。事件收到信号时手动重置信号状态,调用Set()方法释放信号。在调用ReSet()方法前,在此事件等待句柄上的一个或多个等待线程(阻塞线程)收到信号,立即继续执行,并且此时的等待事件句柄一直时保持信号状态(终止状态)。这里有个注意点,EventReseMode.ManualReset等待句柄上有一个或多个等待线程,我们要注意Rese()的时机,等待线程恢复执行前是需要一定的执行时间的,我们无法判断那个等待线程恢复到执行前,在调用Reset()方法可能会中断等待线程的执行。如果我们希望在所有的等待线程都执行完后开启新的线程,就必须将他组织到等待线程都完成后去发送新的信号量执行新的任务。
这里我们可以看看官方的说法

private static EventWaitHandle EventWaitHandle=new EventWaitHandle(false,EventResetMode.ManualReset);
Thread thread1 = new Thread(EventWaitHandle1);
Thread thread2 = new Thread(EventWaitHandle2);
Thread thread3 = new Thread(EventWaitHandle3);
thread1.Start();
thread2.Start();
thread3.Start();
EventWaitHandle.Set();
Thread thread4 = new Thread(EventWaitHandl4);
Thread.Sleep(1000);
thread4.Start();
EventWaitHandle.Reset();
private static void EventWaitHandle1()
{
EventWaitHandle.WaitOne();
Thread.Sleep(1000);
Console.WriteLine("我是第1个EventWaitHandle");
}
private static void EventWaitHandle2()
{
EventWaitHandle.WaitOne();
Thread.Sleep(2000);
Console.WriteLine("我是第2个EventWaitHandle");
}
private static void EventWaitHandle3()
{
EventWaitHandle.WaitOne();
Thread.Sleep(3000);
Console.WriteLine("我是第3个EventWaitHandle");
}
private static void EventWaitHandl4()
{
Thread.Sleep(3000);
EventWaitHandle.WaitOne();
Console.WriteLine("我是第4个EventWaitHandle");
}
这里着重说一下EventWaitHandle4,从上面可以看到,在thread4线程开始后就开始调用了Reset方法,并且在EventWaitHandle里面休眠了三秒,这时候EventWaitHandle无法接收到信号量会一直等待下去直到接收到新的信号量。
Semaphore
Semaphore 可以限制同时进入的线程数量。Semaphore 的构造函数有两个int 类型的参数,第一是指允许同时进入线程的个数,第二个是指最多与同时进入线程的个数,并且第二个参数时不能小于第一个参数(毕竟同时进入的不能大于最大能容纳下的)。WaitOne()方法这里的与上面几个信号量有点小小的不同,每调用一次Semaphore释放的信号灯数量减一,当信号灯数量为0时会阻塞线程,Release()方法会对我们的信号灯数量进行加一操作(释放信号灯),也可以调用Release(int i)来指定释放的信号灯数量。这里有个注意点,我们可以在程序中多次调用Release方法(),但要保证在程序中释放的信号量不能大于最大信号量。
private static Semaphore semaphore = new Semaphore(2, 5);//本地信号灯
for (var i = 0; i < 12; i++)
{
Thread thread = new Thread(new ParameterizedThreadStart(Semaphorehandle));
thread.Start(i);
}
private static void Semaphorehandle(Object i)
{
semaphore.WaitOne();
Console.WriteLine((int)i + "进入了线程");
Thread.Sleep(2000);
Console.WriteLine((int)i + "准备离开线程");
if ((int)i >1)
{
Console.WriteLine(semaphore.Release());
return;
}
semaphore.Release(2);
}
这里插一句——多线程执行是没有特定的顺序的、是不可预测的。
Semaphore信号灯有两种:本地信号灯和命名系统信号灯。本地信号灯仅存在于进程中(上面的例子中使用的是本地信号灯)。命名系统信号灯是存在与整个操作系统的,一般用于同步进程的活动。
c#多线程的信号量就先到这了。
也可以看大佬的教程
本文涉及到的Demo代码
关于c#多线程中的几个信号量的更多相关文章
- c#语言-多线程中的锁系统(一)
介绍 平常在多线程开发中,总避免不了线程同步.本篇就对net多线程中的锁系统做个简单描述. 目录 一:lock.Monitor 1:基础. 2: 作用域. ...
- Java基础教程:多线程基础(6)——信号量(Semaphore)
Java基础教程:多线程基础(6)——信号量(Semaphore) 信号量 信号量(Semaphore)由一个值和一个指针组成,指针指向等待该信号量的进程.信号量的值表示相应资源的使用情况.信号量S≥ ...
- c#初学-多线程中lock用法的经典实例
本文转载自:http://www.cnblogs.com/promise-7/articles/2354077.html 一.Lock定义 lock 关键字可以用来确保代码块完成运行,而不会被 ...
- iOS多线程中,队列和执行的排列组合结果分析
本文是对以往学习的多线程中知识点的一个整理. 多线程中的队列有:串行队列,并发队列,全局队列,主队列. 执行的方法有:同步执行和异步执行.那么两两一组合会有哪些注意事项呢? 如果不是在董铂然博客园看到 ...
- 解决DataGridView在多线程中无法显示滚动条的问题
在多线程中对DataGridView指定 DataSource 来填充数据,更新数据的时候,会导致DataGridView出现假死,显示错误或者滚动条无法显示的问题,在保证了DataGridView的 ...
- iOS开发——多线程OC篇&多线程中的单例
多线程中的单例 #import "DemoObj.h" @implementation DemoObj static DemoObj *instance; // 在iOS中,所有对 ...
- [C#学习]在多线程中如何调用Winform[转]
问题的产生: 我的WinForm程序中有一个用于更新主窗口的工作线程(worker thread),但文档中却提示我不能在多线程中调用这个form(为什么?),而事实上我在调用时程序常常会崩掉.请问如 ...
- Java多线程中易混淆的概念
概述 最近在看<ThinKing In Java>,看到多线程章节时觉得有一些概念比较容易混淆有必要总结一下,虽然都不是新的东西,不过还是蛮重要,很基本的,在开发或阅读源码中经常会遇到,在 ...
- Java多线程中变量的可见性
之所以写这篇博客, 是因为在csdn上看到一个帖子问的就是这个问题. 废话不多说, 我们先看看他的代码(为了减少代码量, 我将创建线程并启动的部分修改为使用方法引用). 1 2 3 4 5 6 7 8 ...
随机推荐
- jboss学习4-jboss7开发配置指南
1 Jboss7下载与安装1.1 官方下载路径:http://www.jboss.org/jbossas/downloads,目前最新稳定版本为7.1.1 final,分别有zip和 ...
- 小程序生成海报demo
效果图: <view class='poste_box' id='canvas-container' style="margin:0 auto;border-radius:16rpx; ...
- nodeJs入门的第一节课
nodejs是什么? nodejs的架构模式以及优缺点 nodejs异步IO nodejs事件驱动 nodejs单线程 nodejs应用场景 一.nodejs是什么? 1.1nodejs是一个开源的. ...
- 【Python打包成exe方法】——已解决导入第三方包无法打包的问题
前言 在我们写代码的过程中,我们开发的脚本一般都会用到一些第三方包,可能别人也需要用到我们的脚本,如果我们将我们的xx.py文件发给他,他是不能直接用的,他还需要安装python解释器,甚至还要安 ...
- Struts2-获取值栈对象与结构
1.获取值栈对象有多种方式 (1)使用ActionContext类里面的方法获取值栈对象 @Override public String execute(){ //1.获取ActionContext类 ...
- crm单元测试使用
Action使用 使用paramBag传递入参,填写入参名,入参值,后使用 serviceProvider传入插件. Assert.AreEqual(this.output["state&q ...
- CPU乱序执行问题
CPU为了提高执行效率,会在一条指令执行的过程中(比如去内存读数据,读数据的过程相较于CPU的执行速度慢100倍以上,cpu处于等待状态),这个时候cpu会分析接下来的指令是否正在执行的指令相关联,如 ...
- Nuxt.js(二、解决首屏速度与SEO)
Nuxt.js(二.解决首屏速度与SEO) 1.普通的Vue SPA初始化加载速度很慢 在传统的web应用中,当用户请求一个页面时,服务器直接返回一个html文件,浏览器直接渲染出来.但是,在vue应 ...
- 【直播回顾】OpenHarmony知识赋能第四期第四课——音频驱动开发
3月31日晚上19点,知识赋能第四期直播的第四节,也是本期最后一节直播课<OpenHarmony标准系统HDF框架之音频驱动开发>,在OpenHarmony开发者成长计划社群内成功举办 ...
- js常用框架原理
(function(){ //存储已经创建的模块 var moduleMap = {}; //判断是否已经加载过 var fileMap = {}; ...