C# 应用 - 多线程 6) 处理同步数据之手动同步 AutoResetEvent 和 ManualResetEvent
1. 类的关系
- AutoResetEvent 和 ManualResetEvent 都继承自 System.Threading.EventWaitHandle 类(EventWaitHandle 继承自 WaitHandle);
- 用于线程交互 (或线程信号)
2. 常用方法
2.1 WaitHandle 几个常用的方法
- public virtual bool WaitOne(int millisecondsTimeout); //阻止当前线程 ,直到当前 System.Threading.WaitHandle 收到信号,或直到 millisecondsTimeout 后。
- public virtual bool WaitOne(); //阻止当前线程,直到当前 System.Threading.WaitHandle 收到信号。
- public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout);
- public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout);
2.2 EventWaitHandle 几个常用的方法
- public bool Set();
将事件设置为有信号,从而允许一个或多个等待线程继续执行 - public bool Reset();
将事件设置为非终止状态,从而导致线程受阻
2.3 AutoResetEvent
- 该类的定义:通知正在等待的线程已发生事件
- public AutoResetEvent(bool initialState); //initialState 指是否将初始状态设置为终止状态的类
2.4 ManualResetEvent
- 该类的定义:通知一个或多个正在等待的线程已发生事件
- public ManualResetEvent(bool initialState);
3. 使用逻辑
3.1 AutoResetEvent 的使用逻辑
- 实例化 AutoResetEvent 时,需要设置初始状态是否为终止状态;
- 若遇到状态为非终止状态的 AutoResetEvent 的 Wait 系列方法(如 WaitOne()),将 阻止当前线程继续往下执行,直到当前 AutoResetEvent 收到信号;
- 收到信号后,保持终止状态并释放一个等待线程,让其继续往下执行,然后自动返回非终止状态。
3.2 ManualResetEvent 的使用逻辑
- 实例化 ManualResetEvent 时,需要设置初始状态是否为终止状态;
- 若遇到状态为非终止状态的 ManualResetEvent 的 Wait 系列方法(如 WaitOne()),将 阻止当前线程继续往下执行,直到当前 ManualResetEvent 收到信号;
- 收到信号后,所有等待线程都被释放,可以继续往下执行;
- 跟 AutoResetEvent 不同的是,收到信号后,ManualResetEvent 将继续保持信号,直到通过手动调用 Reset() 方法重置 ManualResetEvent 的状态为非终止。
4. 例子
4.1 AutoResetEvent
using System;
using System.Threading;
class Example
{
private static AutoResetEvent event_1 = new AutoResetEvent(true);
private static AutoResetEvent event_2 = new AutoResetEvent(false);
static void Main()
{
for (int i = 1; i < 4; i++)
{
Thread t = new Thread(ThreadProc);
t.Name = "Thread_" + i;
t.Start();
}
Thread.Sleep(250);
for (int i = 0; i < 2; i++)
{
Console.WriteLine("按任意键释放另一个线程");
Console.ReadLine();
event_1.Set();
Thread.Sleep(250);
}
Console.WriteLine("\r\n至此所有线程都在等待 AutoResetEvent #2.");
for (int i = 0; i < 3; i++)
{
Console.WriteLine("按任意键释放另一个线程");
Console.ReadLine();
event_2.Set();
Thread.Sleep(250);
}
}
static void ThreadProc()
{
string name = Thread.CurrentThread.Name;
Console.WriteLine("{0} 等待 AutoResetEvent #1.", name);
event_1.WaitOne();
Console.WriteLine("{0} 被 AutoResetEvent #1 释放.", name);
Console.WriteLine("{0} 等待 AutoResetEvent #2.", name);
event_2.WaitOne();
Console.WriteLine("{0} 被 from AutoResetEvent #2 释放.", name);
Console.WriteLine("{0} 结束.", name);
}
}
/* 上面例子将输出如下:
Thread_1 等待 AutoResetEvent #1.
Thread_1 被 AutoResetEvent #1 释放.
Thread_1 等待 AutoResetEvent #2.
Thread_2 等待 AutoResetEvent #1.
Thread_3 等待 AutoResetEvent #1.
按任意键释放另一个线程
Thread_2 被 AutoResetEvent #1 释放.
Thread_2 等待 AutoResetEvent #2.
按任意键释放另一个线程
Thread_3 被 AutoResetEvent #1 释放.
Thread_3 等待 AutoResetEvent #2.
至此所有线程都在等待 AutoResetEvent #2.
按任意键释放另一个线程.
Thread_1 被 from AutoResetEvent #2 释放.
Thread_1 结束.
按任意键释放另一个线程.
Thread_2 被 from AutoResetEvent #2 释放.
Thread_2 结束.
按任意键释放另一个线程.
Thread_3 被 from AutoResetEvent #2 释放.
Thread_3 结束.
*/
4.2 ManualResetEvent
using System;
using System.Threading;
public class Example
{
private static ManualResetEvent mre = new ManualResetEvent(false);
static void Main()
{
Console.WriteLine("\n开启 3 个被 ManualResetEvent 阻塞的线程\n");
for (int i = 0; i <= 2; i++)
{
Thread t = new Thread(ThreadProc);
t.Name = "Thread_" + i;
t.Start();
}
Thread.Sleep(500);
Console.WriteLine("\n所有线程已开启,请按下 enter 键发送信号从而一次性释放所有线程\n");
Console.ReadLine();
mre.Set();
Thread.Sleep(500); // 主线程等待 500 毫秒让子线程先执行完毕
Console.WriteLine("\n当 ManualResetEvent 是有信号时,即使遇到 WaitOne() 也不会被阻塞:\n");
for (int i = 3; i <= 4; i++)
{
Thread t = new Thread(ThreadProc);
t.Name = "Thread_" + i;
t.Start();
}
Thread.Sleep(500);
Console.WriteLine("\n请按下 enter 键盘触发 Reset() 方法, 从而后面的线程在遇到 WaitOne() 时继续被阻塞.\n");
Console.ReadLine();
mre.Reset();
Thread t5 = new Thread(ThreadProc);
t5.Name = "Thread_5";
t5.Start();
Thread.Sleep(500);
Console.WriteLine("\n请按下 enter 键发送信号.");
Console.ReadLine();
mre.Set();
Console.ReadLine();
}
private static void ThreadProc()
{
string name = Thread.CurrentThread.Name;
Console.WriteLine(name + " starts and calls mre.WaitOne()");
mre.WaitOne();
Console.WriteLine(name + " ends.");
}
}
/* 上面例子将输出如下:
开启 3 个被 ManualResetEvent 阻塞的线程
Thread_0 starts and calls mre.WaitOne()
Thread_1 starts and calls mre.WaitOne()
Thread_2 starts and calls mre.WaitOne()
所有线程已开启,请按下 enter 键发送信号从而一次性释放所有线程
Thread_0 ends.
Thread_1 ends.
Thread_2 ends.
当 ManualResetEvent 是有信号时,即使遇到 WaitOne() 也不会被阻塞:
Thread_3 starts and calls mre.WaitOne()
Thread_3 ends.
Thread_4 starts and calls mre.WaitOne()
Thread_4 ends.
请按下 enter 键盘触发 Reset() 方法, 从而后面的线程在遇到 WaitOne() 时继续被阻塞.
Thread_5 starts and calls mre.WaitOne()
请按下 enter 键发送信号.
Thread_5 ends.
*/
C# 应用 - 多线程 6) 处理同步数据之手动同步 AutoResetEvent 和 ManualResetEvent的更多相关文章
- C#多线程同步事件及等待句柄AutoResetEvent 和 ManualResetEvent
最近捣鼓了一下多线程的同步问题,发现其实C#关于多线程同步事件处理还是很灵活,这里主要写一下,自己测试的一些代码,涉及到了AutoResetEvent 和 ManualResetEvent,当然还有也 ...
- 多线程中的锁系统(三)-WaitHandle、AutoResetEvent、ManualResetEvent
本章主要介绍下基于内核模式构造的线程同步方式,事件,信号量. 阅读目录: 理论 WaitHandle AutoResetEvent ManualResetEvent 总结 理论 Windows的线程同 ...
- 原生Redis跨数据中心双向同步优化实践
一.背景 公司基于业务发展以及战略部署,需要实现在多个数据中心单元化部署,一方面可以实现多数据中心容灾,另外可以提升用户请求访问速度.需要保证多数据中心容灾或者实现用户就近访问的话,需要各个数据中心拥 ...
- JAVA之旅(十三)——线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this
JAVA之旅(十三)--线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this 我们继续上个篇幅接着讲线程的知识点 一.线程的安全性 当我们开启四个窗口(线程 ...
- 多线程入门-第七章-线程的同步Synchronized
/* 异步编程模型:两个线程执行自己的,互不影响. 同步编程模型:t1和t2执行,t2必须等t1执行结束之后才能执行. 为什么要线程同步? 1.为了数据的安全,尽管应用程序的使用率降低,但是为了保证数 ...
- Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) JAVA日志的前世今生 .NET MVC采用SignalR更新在线用户数 C#多线程编程系列(五)- 使用任务并行库 C#多线程编程系列(三)- 线程同步 C#多线程编程系列(二)- 线程基础 C#多线程编程系列(一)- 简介
Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) 一.前言 由于本篇文章较长,所以下面给出内容目录方便跳转阅读,当然也可以用博客页面最右侧的文章目录导航栏进行跳转查阅. 一.前言 ...
- Java线程和多线程(三)——线程安全和同步
线程安全在Java中是一个很重要的课题.Java提供的多线程环境支持使用Java线程.我们都知道多线程共享一些对象实例的话,可能会在读取和更新共享数据的事后产生数据不一致问题. 线程安全 之所以会产生 ...
- centos6.5下部署sersync+rsync --daemon同步数据
rsync --daemon端配置 [root@rsync-daemon etc]# /etc/init.d/iptables stop [root@rsync-daemon ~]# dos2unix ...
- 巧用 JuiceFS Sync 命令跨云迁移和同步数据
近年来,云计算已成为主流,企业从自身利益出发,或是不愿意被单一云服务商锁定,或是业务和数据冗余,或是出于成本优化考虑,会尝试将部分或者全部业务从线下机房迁移到云或者从一个云平台迁移到另一个云平台,业务 ...
随机推荐
- 详解Go语言I/O多路复用netpoller模型
转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com 本文使用的go的源码15.7 可以从 Go 源码目录结构和对应代码文件了解 Go 在不同 ...
- Dapr微服务应用开发系列1:环境配置
题记:上篇Dapr系列文章简要介绍了Dapr,这篇来谈一下开发和运行环境配置 本机开发环境配置 安装Docker 为了方便进行Dapr开发,最好(其实不一定必须)首先在本机(开发机器)上安装Docke ...
- 在.NET中体验GraphQL
前言 以前需要提供Web服务接口的时候,除了标准的WEBAPI形式,还考虑了OData.GraphQL等形式,虽然实现思路上有很大的区别,但对使用方来说,都是将查询的主动权让渡给了前端,让调用方能够更 ...
- 僵尸进程 & 孤儿进程
参考博文 基本概念 僵尸进程:是所有进程都会进入的一种进程状态,子进程退出,而父进程并没有调用 wait() 或 waitpid() 获取子进程的状态信息,那么子进程的 PID 和 进程描述符 等资源 ...
- SSH 密钥认证
目录 SSH协议概述 SSH 和 Telnet 的区别 SSH 相关命令 SSH 验证方式 基于密钥的安全认证 SSH 优化 expect 脚本免交互登录 sshpass 免交互登录 SSH协议概述 ...
- 使VS开发的程序在Win7系统运行时自动提升权限
软件开发时,总是会遇到在Win7系统上运行不起来或者异常的情况,这通常是用户的权限不够引起的. 下面提供一个可以使程序运行时,自动提升用户权限的方法. 1.右键点击启动项目,单击"属性&qu ...
- 全局ID生成--雪花算法
分布式ID常见生成策略: 分布式ID生成策略常见的有如下几种: 数据库自增ID. UUID生成. Redis的原子自增方式. 数据库水平拆分,设置初始值和相同的自增步长. 批量申请自增ID. 雪花算法 ...
- CSS selector All In One
CSS selector All In One CSS selector https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors ...
- 高阶类 & HOC & anonymous class extends
高阶类 & HOC & anonymous class extends js 匿名 class extends / mix-ins / 多继承 高阶函数 HOF, 接收一个 funct ...
- JavaScript 设计模式: 发布者-订阅者模式
JavaScript 设计模式: 发布者-订阅者模式 发布者-订阅者模式 https://github.com/Kelichao/javascript.basics/issues/22 https:/ ...