详解C# 线程的挂起与唤醒
作者:jack_Meng 发布时间:2023-03-12 12:40:02
如果说C#和C++有什么不同,博主不得不说,对于异步的支持程度是C#的一一个伟大的进步。
其实早期的C++都没有异步,并发的概念。博主第一次使用C++创建异步程序的时候,是使用boost库的内容进行实现的。相对而言,C#对于异步的支持可以说是相当的好。相信很多名词大家都很耳熟能详,比如说Thread,BeginInvoke,Delegate,backgroundworker等等。。。其实楼主在使用了这么多的异步操作过程中,还是觉得backgroudworker比较好用。
当然,我们今天要说的和上面的无关。讲述的是如何在线程中进行挂起唤醒操作。
假设,有一个Thread现在需要挂起,等到合适的时候再唤醒那么这个线程(消费者模式)。如果大家需要用Suspend,Resume操作,我建议还是要思考再三。以下是msdn原话(https://msdn.microsoft.com/zh-cn/library/system.threading.thread.suspend(v=vs.110).aspx):
Do not use the Suspend and Resume methods to synchronize the activities of threads. You have no way of knowing what code a thread is executing when you suspend it. If you suspend a thread while it holds locks during a security permission evaluation, other threads in the AppDomain might be blocked. If you suspend a thread while it is executing a class constructor, other threads in the AppDomain that attempt to use that class are blocked. Deadlocks can occur very easily.
本篇文章要说的线程挂起与继续的方式其实是利用AutoResetEvent和ManualResetEvent的方法进行堵塞和继续的。
在介绍AutoResetEvent和ManualResetEvent之前,先介绍一个概念,就是线程中Set()和Reset()的区别。
set:指的是将一个事件设置为有信号,那么被这个事件堵塞的线程就会继续下去。
reset:指的是将一个事件设置为无信号,那么尝试继续的事件就会被堵塞。
一,AutoResetEvent类
这个类的字面意思就能够解释一切:自动reset的事件,就是这个事件一旦set之后,如果某个线程堵塞被继续了,那么就会自动reset。下一次如果尝试继续,依然会被堵塞。
其中AutoResetEvent类的构造函数有一个参数 是bool型。
MSDN的解释是:
Initializes a new instance of the AutoResetEvent class with a Boolean value indicating whether to set the initial state to signaled.
如果这个参数是true,那么第一次尝试继续就不会被阻塞。如果这个参数是false,那么第一次尝试继续就会被堵塞。
以下是测试代码,取自MSDN:
using System;
using System.Threading;
// Visual Studio: Replace the default class in a Console project with
// the following class.
class Example
{
private static AutoResetEvent event_1 = new AutoResetEvent(true);
private static AutoResetEvent event_2 = new AutoResetEvent(false);
static void Main()
{
Console.WriteLine("Press Enter to create three threads and start them.\r\n" +
"The threads wait on AutoResetEvent #1, which was created\r\n" +
"in the signaled state, so the first thread is released.\r\n" +
"This puts AutoResetEvent #1 into the unsignaled state.");
Console.ReadLine();
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("Press Enter to release another thread.");
Console.ReadLine();
event_1.Set();
Thread.Sleep(250);
}
Console.WriteLine("\r\nAll threads are now waiting on AutoResetEvent #2.");
for (int i = 0; i < 3; i++)
{
Console.WriteLine("Press Enter to release a thread.");
Console.ReadLine();
event_2.Set();
Thread.Sleep(250);
}
// Visual Studio: Uncomment the following line.
//Console.Readline();
}
static void ThreadProc()
{
string name = Thread.CurrentThread.Name;
Console.WriteLine("{0} waits on AutoResetEvent #1.", name);
event_1.WaitOne();
Console.WriteLine("{0} is released from AutoResetEvent #1.", name);
Console.WriteLine("{0} waits on AutoResetEvent #2.", name);
event_2.WaitOne();
Console.WriteLine("{0} is released from AutoResetEvent #2.", name);
Console.WriteLine("{0} ends.", name);
}
}
其中,AutoResetEvent.WaitOne()这个方法就是线程中尝试继续。如果没有SET信号,那么就会一直阻塞,如果收到Set信号该线程就会继续。但是因为是AutoResetEvent,所以下一次waitOne依然会被阻塞。
上面代码的输出结果是:
Press Enter to create three threads and start them.
The threads wait on AutoResetEvent #1, which was created
in the signaled state, so the first thread is released.
This puts AutoResetEvent #1 into the unsignaled state.
Thread_1 waits on AutoResetEvent #1.
Thread_1 is released from AutoResetEvent #1.
Thread_1 waits on AutoResetEvent #2.
Thread_3 waits on AutoResetEvent #1.
Thread_2 waits on AutoResetEvent #1.
Press Enter to release another thread.
Thread_3 is released from AutoResetEvent #1.
Thread_3 waits on AutoResetEvent #2.
Press Enter to release another thread.
Thread_2 is released from AutoResetEvent #1.
Thread_2 waits on AutoResetEvent #2.
All threads are now waiting on AutoResetEvent #2.
Press Enter to release a thread.
Thread_2 is released from AutoResetEvent #2.
Thread_2 ends.
Press Enter to release a thread.
Thread_1 is released from AutoResetEvent #2.
Thread_1 ends.
Press Enter to release a thread.
Thread_3 is released from AutoResetEvent #2.
Thread_3 ends.
二,ManualResetEvent
ManualResetEvent和AutoResetEvent大部分概念都是相同的,最大的不同就是一个是自动reset一个是手动reset。也就是说,如果使用ManualResetEvent类,一旦Set之后,所有已经阻塞的线程(waitone())都会继续。而且之后调用waitone的线程也不会被堵塞,除非手动再次Reset。也就是说,这个类是手动开启关闭信号的事件。
以下是测试代码,取自MSDN:
using System;
using System.Threading;
public class Example
{
// mre is used to block and release threads manually. It is
// created in the unsignaled state.
private static ManualResetEvent mre = new ManualResetEvent(false);
static void Main()
{
Console.WriteLine("\nStart 3 named threads that block on a 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("\nWhen all three threads have started, press Enter to call Set()" +
"\nto release all the threads.\n");
Console.ReadLine();
mre.Set();
Thread.Sleep(500);
Console.WriteLine("\nWhen a ManualResetEvent is signaled, threads that call WaitOne()" +
"\ndo not block. Press Enter to show this.\n");
Console.ReadLine();
for(int i = 3; i <= 4; i++)
{
Thread t = new Thread(ThreadProc);
t.Name = "Thread_" + i;
t.Start();
}
Thread.Sleep(500);
Console.WriteLine("\nPress Enter to call Reset(), so that threads once again block" +
"\nwhen they call WaitOne().\n");
Console.ReadLine();
mre.Reset();
// Start a thread that waits on the ManualResetEvent.
Thread t5 = new Thread(ThreadProc);
t5.Name = "Thread_5";
t5.Start();
Thread.Sleep(500);
Console.WriteLine("\nPress Enter to call Set() and conclude the demo.");
Console.ReadLine();
mre.Set();
// If you run this example in Visual Studio, uncomment the following line:
//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.");
}
}
输出结果是:
Start 3 named threads that block on a ManualResetEvent:
Thread_0 starts and calls mre.WaitOne()
Thread_1 starts and calls mre.WaitOne()
Thread_2 starts and calls mre.WaitOne()
When all three threads have started, press Enter to call Set()
to release all the threads.
Thread_2 ends.
Thread_0 ends.
Thread_1 ends.
When a ManualResetEvent is signaled, threads that call WaitOne()
do not block. Press Enter to show this.
Thread_3 starts and calls mre.WaitOne()
Thread_3 ends.
Thread_4 starts and calls mre.WaitOne()
Thread_4 ends.
Press Enter to call Reset(), so that threads once again block
when they call WaitOne().
Thread_5 starts and calls mre.WaitOne()
Press Enter to call Set() and conclude the demo.
Thread_5 ends.
ManualResetEvent类的输出结果与AutoResetEvent输出结果最大的不同是在于:
如果不手动Reset,一旦调用Set方法,那么ManualResetEvent.WaitOne()就不会堵塞。
但是,AutoResetEvent会自动Reset,所以哪怕不手动Reset,每一次AutoResetEvent.WaitOne()都需要Set方法进行触发以继续线程。
来源:https://www.cnblogs.com/mq0036/p/14202385.html
猜你喜欢
- Java绘图中,显示文字的方法主要有三种:(1)drawString(String str,int x,int y):在指定的位置显示字符串
- springBoot项目启动多个实例今天碰到一个需求是,将一个服务提供者启动两个实例,一个实例对外,一个实例对内,对内价格有折扣,两个实例通
- Invoke Phing targets这个插件主要是读取xml形式包括自动化测试打包部署的配置文件,然后根据流程走下来。用ph
- 上一章节回顾:Netty分布式源码分析监听读事件概述pipeline, 顾名思义, 就是管道的意思, 在net
- 核心考点:链表操作,临界条件检查,特殊情况处理在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针
- 之前写过一篇获取properties文件里面的值:Springboot 指定获取自己写的配置properties文件的值www.jb51.n
- 本文实例分析了C#动态生成DropDownList执行失败原因。分享给大家供大家参考。具体如下:今天研究DDL控件的动态生成的时候遇到了点问
- 背景最近好几个项目在运行过程中客户都提出文件上传大小的限制能否设置的大一些,用户经常需要上传好几个G的资料文件,如图纸,视频等,并且需要在上
- 概述ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存。ArrayList不是
- 使用unity制作瞄准镜,供大家参考,具体内容如下一、创建场景在Hierarchy窗口中使用Plane和Cube创建场景,并调整摄像机位置二
- 这几天用winform做了一个设置壁纸的小工具, 为图片添加当月的日历并设为壁纸,可以手动设置壁纸,也可以定时设置壁纸,最主要的特点是在图片
- 本文介绍通过Java程序批量替换PDF中的指定文本内容。程序环境准备如下:程序使用环境如图,需要注意的是,本文使用了免费版的PDF jar工
- 将索引设置为只读可能听起来很奇怪,但在 Elasticsearch 中执行此类操作是可能的。想象一下这样一种情况,你特别需要限制对索引的写入
- 在有些开发场景,需要对 List 对象列表进行过滤处理,并将有用的数据存放到Map中。例如:告警对象,包含告警uuid(alarmUuid)
- 系列文章已完成,目录如下:jdk-logging log4j logback日志系统实现机制原理介绍commons-lo
- Hadoop环境搭建详见此文章https://www.jb51.net/article/33649.htm。我们已经知道Hadoop能够通过
- 方式一:if语句控制// 例如:Column( mainAxisAlig
- MyBatis在注解上使用动态SQL1、用script标签包围然后像xml语法一样书写@Select({"<script&g
- 1. Mybatis的@param注解自定义对象也用@param注解注:使用@param注解,mapper.xml 不加parameterT
- 循环队列结构队列特点队列为一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,