关于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 ...
随机推荐
- Javascript中数组的判断方法
摘要: 1.数组检测的方法: 1) typeof . 2) instanceof . 3) constructor . 4) Object.prototype.toString. 5) Array.i ...
- 如何在 Java 中实现无向环和有向环的检测
无向环 一个含有环的无向图如下所示,其中有两个环,分别是 0-2-1-0 和 2-3-4-2: 要检测无向图中的环,可以使用深度优先搜索.假设从顶点 0 出发,再走到相邻的顶点 2,接着走到顶点 2 ...
- C++---变量、数据类型和运算符
内存 计算机使用内存来记忆或存储计算时所使用的的数据. 计算机执行程序时, 组成程序的指令和程序所操作的数据都必须存放在某个地方, 而这个地方就是计算机的内存, 也称为主存, 或随机访问存储器(RAM ...
- eclipse 下 SpringBoot 工程使用Maven打包
eclipse 下 SpringBoot 工程使用Maven打包 1. pom.xml 添加打包配置 点击查看代码 <!-- 打包使用 --> <build> <plug ...
- 用js实现倒计时效果
首先获得两个时间的时间戳 var newdate = new Date('2021-01-22 21:25:00').getTime(); var olddate = new Date().getTi ...
- [译]ng指令中的compile与link函数解析 转
通常大家在使用ng中的指令的时候,用的链接函数最多的是link属性,下面这篇文章将告诉大家complie,pre-link,post-link的用法与区别. 原文地址 angularjs里的指令非常神 ...
- Hyperledger Fabric 部署在多个主机上
前言 在实验Hyperledger Fabric无排序组织以Raft协议启动多个Orderer服务.TLS组织运行维护Orderer服务中,我们已经完成了使用提供 TLS-CA 服务的 council ...
- 【大话云原生】煮饺子与docker、kubernetes之间的关系
云原生的概念最近非常火爆,企业落地云原生的愿望也越发强烈.看过很多关于云原生的文章,要么云山雾罩,要么曲高和寡. 所以笔者就有了写<大话云原生>系列文章的想法,期望用最通俗.简单的语言说明 ...
- MySQL数据库常识之储存引擎
我的博客 储存引擎分类 show engines; 这个命令可以查看数据库的数据引擎,可以看到InnoDB是默认的引擎. 命令除了在终端运行,也可以在查询数据库可视化工具中运行. 而,(我是5.7版本 ...
- Linux网络流量相关
一直以来对Linux网络这块都感觉比较乱 遇到一个UDP丢包的问题:在测试中,一台VM虚拟机,CPU利用率55%左右,内存利用率7%左右,网卡流量也远没到限制的时候出现了丢包情况 使用netstat ...