C#线程同步--线程通信
问题抽象:当某个操作的执行必须依赖于另一个操作的完成时,需要有个机制来保证这种先后关系。
线程通信方案:ManualResetEventSlim、ManualResetEvent、AutoResetEvent
方案特性:提供线程通知的能力,没有接到通知前,线程必须等待,有先后顺序。
1、ManualResetEvent类
对象有两种信号量状态True和False。构造函数设置初始状态。简单来说,
◆ 如果构造函数由true创建,则第一次WaitOne()不会阻止线程的执行,而是等待Reset后的第二次WaitOne()才阻止线程执行。
◆ 如果构造函数有false创建,则WaitOne()必须等待Set()才能往下执行。
一句话总结就是:是否忽略第一次阻塞。
方法如下:
◆ WaitOne:该方法用于阻塞线程,默认是无限期的阻塞,支持设置等待时间,如果超时就放弃阻塞,不等了,继续往下执行;
◆ Set:手动修改信号量为True,也就是恢复线程执行;
◆ ReSet:重置状态; 重置后,又能WaitOne()啦
using System;
using System.Threading; namespace ConsoleApp1
{
class Program
{
//一开始设置为false才会等待收到信号才执行
static ManualResetEvent mr = new ManualResetEvent(false);
public static void Main()
{
Thread t = new Thread(Run);
//启动辅助线程
t.Start();
//等待辅助线程执行完毕之后,主线程才继续执行
Console.WriteLine("主线程一边做自己的事,一边等辅助线程执行!" + DateTime.Now.ToString("mm:ss"));
mr.WaitOne();
Console.WriteLine("收到信号,主线程继续执行" + DateTime.Now.ToString("mm:ss"));
Console.ReadKey();
} static void Run()
{
//模拟长时间任务
Thread.Sleep();
Console.WriteLine("辅助线程长时间任务完成!" + DateTime.Now.ToString("mm:ss"));
mr.Set();
}
}
}
Program
在思维上,这个东西可以有两种用法,一种是让主线程等待辅助线程,一种是辅助线程等待主线程。
但无论怎么用,都是让一个线程等待或唤醒另外一个线程。
Reset方法调用示例
using System;
using System.Threading; namespace ConsoleApp1
{
class Program
{
//一开始设置为false,当遇到WaitOne()时,需要Set()才能继续执行
static ManualResetEvent mr = new ManualResetEvent(false); public static void Main()
{
Thread t = new Thread(Run);
Console.WriteLine("开始" + DateTime.Now.ToString("mm:ss"));
t.Start();
mr.WaitOne();
Console.WriteLine("第一次等待完成!" + DateTime.Now.ToString("mm:ss"));
mr.Reset(); //重置后,又能WaitOne()啦
mr.WaitOne();
Console.WriteLine("第二次等待完成!" + DateTime.Now.ToString("mm:ss"));
Console.ReadKey();
} static void Run()
{
mr.Set();
Thread.Sleep();
mr.Set();
}
}
}
Program
如果以上代码不使用Reset,则直接输出第二次等待完成,而不会等待2秒。
2、AutoResetEvent类
AutoResetEvent与ManualResetEvent的区别在于AutoResetEvent 的WaitOne会改变信号量的值为false,让其等待阻塞。
比如说初始信号量为True,如果WaitOne超时信号量将自动变为False,而ManualResetEvent则不会。
第二个区别:
◆ ManualResetEvent:每次可以唤醒一个或多个线程
◆ AutoResetEvent:每次只能唤醒一个线程
using System;
using System.Threading; namespace ConsoleApp1
{
class Program
{
static AutoResetEvent ar = new AutoResetEvent(true);
public static void Main()
{
Thread t = new Thread(Run);
t.Start(); bool state = ar.WaitOne();
Console.WriteLine("当前的信号量状态:{0}", state); state = ar.WaitOne();
Console.WriteLine("再次WaitOne后现在的状态是:{0}", state); state = ar.WaitOne();
Console.WriteLine("再次WaitOne后现在的状态是:{0}", state); Console.ReadKey();
} static void Run()
{
Console.WriteLine("当前时间" + DateTime.Now.ToString("mm:ss"));
}
}
}
Program
AutoResetEvent 允许线程通过发信号互相通信。通常,此通信涉及线程需要独占访问的资源。
线程通过调用 AutoResetEvent 上的 WaitOne 来等待信号。如果 AutoResetEvent 处于非终止状态,则该线程阻塞,并等待当前控制资源的线程,通过调用 Set 发出资源可用的信号。调用 Set 向 AutoResetEvent 发信号以释放等待线程。AutoResetEvent 将保持终止状态,直到一个正在等待的线程被释放,然后自动返回非终止状态。如果没有任何线程在等待,则状态将无限期地保持为终止状态。可以通过将一个布尔值传递给构造函数来控制 AutoResetEvent 的初始状态,如果初始状态为终止状态,则为 true;否则为 false。
通俗的来讲只有等myResetEven.Set()成功运行后,myResetEven.WaitOne()才能够获得运行机会;Set是发信号,WaitOne是等待信号,只有发了信号,等待的才会执行。如果不发的话,WaitOne后面的程序就永远不会执行。下面我们来举一个例子:我去书店买书,当我选中一本书后我会去收费处付钱,
付好钱后再去仓库取书。这个顺序不能颠倒,我作为主线程,收费处和仓库做两个辅助线程,代码如下:
using System;
using System.Threading; namespace ConsoleApp1
{
class TestAutoReseEvent
{
static AutoResetEvent BuyBookEvent = new AutoResetEvent(false);
static AutoResetEvent PayMoneyEvent = new AutoResetEvent(false);
static AutoResetEvent GetBookEvent = new AutoResetEvent(false);
static int number = ; public static void Run()
{
Thread buyBookThread = new Thread(new ThreadStart(BuyBookProc));
buyBookThread.Name = "买书线程";
Thread payMoneyThread = new Thread(new ThreadStart(PayMoneyProc));
payMoneyThread.Name = "付钱线程";
Thread getBookThread = new Thread(new ThreadStart(GetBookProc));
getBookThread.Name = "取书线程"; buyBookThread.Start();
payMoneyThread.Start();
getBookThread.Start(); buyBookThread.Join();
payMoneyThread.Join();
getBookThread.Join(); } static void BuyBookProc()
{
while (number > )
{
Console.WriteLine("{0}:数量{1}", Thread.CurrentThread.Name, number);
PayMoneyEvent.Set();
BuyBookEvent.WaitOne();
Console.WriteLine("------------------------------------------");
number--;
}
} static void PayMoneyProc()
{
while (number > )
{
PayMoneyEvent.WaitOne();
Console.WriteLine("{0}:数量{1}", Thread.CurrentThread.Name, number);
GetBookEvent.Set();
}
} static void GetBookProc()
{
while (number > )
{
GetBookEvent.WaitOne();
Console.WriteLine("{0}:数量{1}", Thread.CurrentThread.Name, number);
BuyBookEvent.Set();
}
}
}
}
TestAutoReseEvent
namespace ConsoleApp1
{
class Program
{
public static void Main()
{
TestAutoReseEvent.Run();
}
}
}
Program
3、ManualResetEventSlim类
ManualResetEventSlim是ManualResetEvent的混合版本,一直保持大门敞开直到手工调用Reset方法,
Set() 相当于打开了大门从而允许准备好的线程接收信号并继续工作
Reset() 相当于关闭了大门 此时已经准备好执行的信号量 则只能等到下次大门开启时才能够执行
using System;
using System.Threading; namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var t1 = new Thread(() => TravelThroughGates("Thread 1", ));
var t2 = new Thread(() => TravelThroughGates("Thread 2", ));
var t3 = new Thread(() => TravelThroughGates("Thread 3", ));
t1.Start();
t2.Start();
t3.Start();
Thread.Sleep(TimeSpan.FromSeconds());
Console.WriteLine("The gates are now open!");
_mainEvent.Set();
Thread.Sleep(TimeSpan.FromSeconds());
_mainEvent.Reset();
Console.WriteLine("The gates have been closed!");
Thread.Sleep(TimeSpan.FromSeconds());
Console.WriteLine("The gates are now open for the second time!");
_mainEvent.Set();
Thread.Sleep(TimeSpan.FromSeconds());
Console.WriteLine("The gates have been closed!");
_mainEvent.Reset();
} static void TravelThroughGates(string threadName, int seconds)
{
Console.WriteLine("{0} falls to sleep {1}", threadName, seconds);
Thread.Sleep(TimeSpan.FromSeconds(seconds));
Console.WriteLine("{0} waits for the gates to open!", threadName);
_mainEvent.Wait();
Console.WriteLine("{0} enters the gates!", threadName);
}
/// <summary>
/// ManualResetEventSlim是ManualResetEvent的混合版本,一直保持大门敞开直到手工调用Reset方法,
/// _mainEvent.Set 相当于打开了大门从而允许准备好的线程接收信号并继续工作
/// _mainEvent.Reset 相当于关闭了大门 此时已经准备好执行的信号量 则只能等到下次大门开启时才能够执行
/// </summary>
static ManualResetEventSlim _mainEvent = new ManualResetEventSlim(false);
}
}
Program
C#线程同步--线程通信的更多相关文章
- 线程:主线程、子线程 同步线程、异步线程 单线程、多线程 System.Threading与System.Windows.Threading
入门-------------------------------------------------------------------------------- 概述与概念 一个C#程序开始 ...
- 线程同步&线程池
线程同步&线程池 线程同步 线程不同步会出现的问题: 当多个线程操作同一资源时,会出现重复操作和和操作不存在的资源的问题,为了规避这一问题就需要线程的同步操作来实现资源的共同使用. 线程同步: ...
- Python并发编程-线程同步(线程安全)
Python并发编程-线程同步(线程安全) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 线程同步,线程间协调,通过某种技术,让一个线程访问某些数据时,其它线程不能访问这些数据,直 ...
- Win32多线程编程(3) — 线程同步与通信
一.线程间数据通信 系统从进程的地址空间中分配内存给线程栈使用.新线程与创建它的线程在相同的进程上下文中运行.因此,新线程可以访问进程内核对象的所有句柄.进程中的所有内存以及同一个进程中其他所有线 ...
- Qt QThread 线程创建,线程同步,线程通信 实例
1. 继承QThread, 实现run()方法, 即可创建线程. 2. 实例1 代码 myThread.h #ifndef MYTHREAD_H #define MYTHREAD_H #includ ...
- c++线程同步和通信
一.线程的创建 你也许会说我一直用CreateThread来创建线程,一直都工作得好好的,为什么要用_beginthreadex来代替CreateThread,下面让我来告诉你为什么. 回答一个 ...
- Linux多线程编程——多线程与线程同步
多线程 使用多线程好处: 一.通过为每种事件类型的处理单独分配线程,可以简化处理异步事件的代码,线程处理事件可以采用同步编程模式,启闭异步编程模式简单 二.方便的通信和数据交换 由于进程之间具有独立的 ...
- C++线程同步的四种方式(Windows)
为什么要进行线程同步? 在程序中使用多线程时,一般很少有多个线程能在其生命期内进行完全独立的操作.更多的情况是一些线程进行某些处理操作,而其他的线程必须对其处理结果进行了解.正常情况下对这种处理结果的 ...
- 33 - 并发编程-线程同步-Event-lock
目录 1 线程同步 1.1 Event 1.1.1 什么是Flag? 1.1.2 Event原理 1.1.3 吃包子 1.2 Lock 1.2.1 lock方法 1.2.2 计数器 1.2.3 非阻塞 ...
随机推荐
- [GIT] 更新.repo目录
cd .repo/manifests/ git co -f git pull
- mysql数据库内容相关操作
第一:介绍 mysql数据内容的操作主要是: INSERT实现数据的插入 UPDATE实现数据的更新 DLETE实现数据的删除 SELECT实现数据的查询. 第二:增(insert) 1.插入完整的数 ...
- 数位dp-Bomb
难受啊!!越做题是越感觉菜,这个又被几个坑给卡住了(只有我这个学渣才会卡) 坑点:1.考虑n是否已包含49,有的话还要再+1. 2, 注意从最高开始考虑时,再判断时要考虑它本身为0的情况,.比如n=5 ...
- 连接Redis_五种数据格式
前面我们已经准备成功开启Redis服务,其端口号为6379,接下来我们就看看如何使用C#语言来操作Redis.就如MongoDB一样,要操作Redis服务,自然就需要下载C#的客户端,这里通过Nuge ...
- 「深度剖析」程序员因为奇葩需求暴打pm,然后被双双开除
想必大家都听说了,这两天关于中国平安一个产品经理因奇葩需求和程序员爆发肢体冲突的事件在朋友圈被刷屏,更有现场打架视频在技术群里疯传. 在这里先带大家简单文字回顾下事情经过,N次打架视频和截图就不给大家 ...
- html笔记第一天
快速生成标签有序ol>li*3无序ul>(li>a{新闻标题})*3定义列表 dl>(dt+dd)*3制作表格table>(tr>td*5)*6pading:3个数 ...
- Flutter
2015年, Google 内部开始测试另一种高性能的编程方式,那就 Google 的 Sky 项目.Sky 项目使用网页开发语言Dart开发原生Android 应用,强调应用的运行速度和与 Web ...
- Mesos源码分析(7): Mesos-Slave的启动
Mesos-Slave的启动是从src/slave/main.cpp中的main函数开始的. 看过了Mesos-Master的启动过程,Mesos-Slave的启动没有那么复杂了. 1. ...
- 使用RSA加密在Python中逆向shell
i春秋翻译小组-Neo(李皓伟) 使用RSA加密在Python中逆向shell 这是一个关于使用RSA加密编程逆向shell的python教程. 我想提一下,这篇文章更多的是关于理解shell中涉及的 ...
- Python内置函数(35)——issubclass
英文文档: issubclass(class, classinfo) Return true if class is a subclass (direct, indirect or virtual) ...