信号量在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#多线程中的几个信号量的更多相关文章

  1. c#语言-多线程中的锁系统(一)

    介绍 平常在多线程开发中,总避免不了线程同步.本篇就对net多线程中的锁系统做个简单描述.   目录 一:lock.Monitor        1:基础.        2: 作用域.       ...

  2. Java基础教程:多线程基础(6)——信号量(Semaphore)

    Java基础教程:多线程基础(6)——信号量(Semaphore) 信号量 信号量(Semaphore)由一个值和一个指针组成,指针指向等待该信号量的进程.信号量的值表示相应资源的使用情况.信号量S≥ ...

  3. c#初学-多线程中lock用法的经典实例

    本文转载自:http://www.cnblogs.com/promise-7/articles/2354077.html 一.Lock定义     lock 关键字可以用来确保代码块完成运行,而不会被 ...

  4. iOS多线程中,队列和执行的排列组合结果分析

    本文是对以往学习的多线程中知识点的一个整理. 多线程中的队列有:串行队列,并发队列,全局队列,主队列. 执行的方法有:同步执行和异步执行.那么两两一组合会有哪些注意事项呢? 如果不是在董铂然博客园看到 ...

  5. 解决DataGridView在多线程中无法显示滚动条的问题

    在多线程中对DataGridView指定 DataSource 来填充数据,更新数据的时候,会导致DataGridView出现假死,显示错误或者滚动条无法显示的问题,在保证了DataGridView的 ...

  6. iOS开发——多线程OC篇&多线程中的单例

    多线程中的单例 #import "DemoObj.h" @implementation DemoObj static DemoObj *instance; // 在iOS中,所有对 ...

  7. [C#学习]在多线程中如何调用Winform[转]

    问题的产生: 我的WinForm程序中有一个用于更新主窗口的工作线程(worker thread),但文档中却提示我不能在多线程中调用这个form(为什么?),而事实上我在调用时程序常常会崩掉.请问如 ...

  8. Java多线程中易混淆的概念

    概述 最近在看<ThinKing In Java>,看到多线程章节时觉得有一些概念比较容易混淆有必要总结一下,虽然都不是新的东西,不过还是蛮重要,很基本的,在开发或阅读源码中经常会遇到,在 ...

  9. Java多线程中变量的可见性

    之所以写这篇博客, 是因为在csdn上看到一个帖子问的就是这个问题. 废话不多说, 我们先看看他的代码(为了减少代码量, 我将创建线程并启动的部分修改为使用方法引用). 1 2 3 4 5 6 7 8 ...

随机推荐

  1. C#编写一个计算器

    编写一个计算器,练习在窗体上添加控件.调整控件的布局,设置或修改控件属性,编写事件处理程序的方法. 代码: using System; using System.Collections.Generic ...

  2. String和int、long、double等基本数据类型的转换

    学习目标: 掌握Java的基本数据类型与String的转换 学习内容: 1.转化规则 String 转 基本数据类型 基本数据类型 变量 = 包装类.Parse基本数据类型(String); // c ...

  3. vuecli中配置webpack加快打包速度

    webpack4中webpack 的DllPlugin插件可以将常见的库文件作为dll文件来,每次打包的时候就不用再次打包库文件了. 但是游鱼西在vuecli中已经去除这个选项,意识到带来的打包速度提 ...

  4. 关于data自定义属性

    新的HTML5标准允许你在普通的元素标签里,嵌入类似data-*的属性,来实现一些简单数据的存取.它的数量不受限制,并且也能由JavaScript动态修改,也支持CSS选择器进行样式设置.这使得dat ...

  5. Nestjs模块机制的概念和实现原理

    1 前言 Nest 提供了模块机制,通过在模块装饰器中定义提供者.导入.导出和提供者构造函数便完成了依赖注入,通过模块树组织整个应用程序的开发.按照框架本身的约定直接撸一个应用程序,是完全没有问题的. ...

  6. 无法访问 CentOS7服务器上应用监听的端口

    无法访问 CentOS7服务器上应用监听的端口 参考资料 云主机上Centos7配置Iptables规则开启80.3306等端口https://blog.csdn.net/qq_37960007/ar ...

  7. Python BeautifulSoup4 爬虫基础、多线程学习

    针对 崔庆才老师 的 https://ssr1.scrape.center 的爬虫基础练习.Threading多线程库.Time库.json库.BeautifulSoup4 爬虫库.py基本语法

  8. form表单请求

    form 表单的acton属性指向url:端口号/(服务器get,post的参数), meyhod='get'/'post'  请求方式,必须要加上name属性. <form action=&q ...

  9. 面试官:为什么Vue中的v-if和v-for不建议一起用?

    一.作用 v-if 指令用于条件性地渲染一块内容.这块内容只会在指令的表达式返回 true值的时候被渲染 v-for 指令基于一个数组来渲染一个列表.v-for 指令需要使用 item in item ...

  10. partTwo自动出题程序第二阶段

    (1)题目避免重复: (2)可定制(数量/打印方式): 代码实现 import java.util.ArrayList;import java.util.Random;import java.util ...