System.Threading.AutoResetEvent用于阻塞和继续执行线程,
关键函数WaitOne()用于阻塞下面的线程代码执行、Set()用于继续执行一次WaitOne()下面的代码
参考官方文档:
https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.autoresetevent?view=net-5.0
AutoResetEvent 类
定义
命名空间:
程序集:
mscorlib.dll, System.Threading.dll
表示线程同步事件在一个等待线程释放后收到信号时自动重置。 此类不能被继承(密封Sealed)。
线程安全性
此类(AutoResetEvent)是线程安全的。
重要说明
AutoResetEvent在多线程编程中,经常用到。当某个线程调用WaitOne方法后,将阻塞WaitOne后面的代码执行,直到收到Set()信号, WaitOne后面的程序就才会继续向下执行。
而且 AutoResetEvent.WaitOne()每次只允许一个线程进入,当某个线程得到信号后,AutoResetEvent会自动又将信号置为不发送状态,其他调用WaitOne的线程只有继续等待.
也就是说,AutoResetEvent一次只唤醒一个线程,其他线程还是堵塞。
AutoResetEvent(bool initialState):构造函数,用一个指示是否将初始状态设置为终止的布尔值初始化该类的新实例。
false:无信号,子线程的WaitOne方法不会被自动调用
true:有信号,子线程的WaitOne方法会被自动调用
Reset():将事件状态设置为非终止状态,导致线程阻止;如果该操作成功,则返回true;否则,返回false。
Set():将事件状态设置为终止状态,允许一个或多个等待线程继续;如果该操作成功,则返回true;否则,返回false。
Set(),其实就相当于一个开关,如果没有执行set()方法,下面的waitOne()就等不到让它执行的通知,这样一来waitOne后面的语句也不会执行了.
WaitOne(): 阻止当前线程,直到收到信号。没有收到Set()信号前,waitOne后面的代码将一直阻塞(死循环等待)
WaitOne(TimeSpan, Boolean) :阻止当前线程,直到当前实例收到信号,使用 TimeSpan 度量时间间隔并指定是否在等待之前退出同步域。
WaitAll():等待全部信号。
测试程序
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
/*
* AutoResetEvent在.Net多线程编程中,经常用到。当某个线程调用WaitOne方法后,将阻塞WaitOne后面的代码执行,直到收到Set()信号, WaitOne后面的程序就才会继续向下执行。
* 而且 AutoResetEvent.WaitOne()每次只允许一个线程进入,当某个线程得到信号后,AutoResetEvent会自动又将信号置为不发送状态,其他调用WaitOne的线程只有继续等待.
* 也就是说,AutoResetEvent一次只唤醒一个线程,其他线程还是堵塞。
AutoResetEvent(bool initialState):构造函数,用一个指示是否将初始状态设置为终止的布尔值初始化该类的新实例。
false:无信号,子线程的WaitOne方法不会被自动调用
true:有信号,子线程的WaitOne方法会被自动调用
Reset():将事件状态设置为非终止状态,导致线程阻止;如果该操作成功,则返回true;否则,返回false。
Set():将事件状态设置为终止状态,允许一个或多个等待线程继续;如果该操作成功,则返回true;否则,返回false。
Set(),其实就相当于一个开关,如果没有执行set()方法,下面的waitOne()就等不到让它执行的通知,这样一来waitOne后面的语句也不会执行了.
WaitOne(): 阻止当前线程,直到收到信号。没有收到Set()信号前,waitOne后面的代码将一直阻塞(死循环等待)
WaitOne(TimeSpan, Boolean) :阻止当前线程,直到当前实例收到信号,使用 TimeSpan 度量时间间隔并指定是否在等待之前退出同步域。
WaitAll():等待全部信号。
*/
namespace AutoResetEventDemo
{
class Program
{
static AutoResetEvent AutoResetEventA = new AutoResetEvent(false);
static AutoResetEvent AutoResetEventB = new AutoResetEvent(false);
static AutoResetEvent AutoResetEventReadWrite = new AutoResetEvent(false);
static void Main(string[] args)
{
//线程A,线程B同时开始执行
Thread threadA = new Thread(MethodA);
threadA.Start();
Thread threadB = new Thread(MethodB);
threadB.Start();
//等待句柄信号:WaitAll函数下面的代码将等待执行,直到线程A、B收到信号
AutoResetEvent.WaitAll(new AutoResetEvent[] { AutoResetEventA, AutoResetEventB });
//如果没有调用AutoResetEventA.Set() 或者 没有调用AutoResetEventB.Set(),Wait后面的代码MethodC()将不会执行
//MethodC继续执行的条件:全部收到线程A和B中的Set()通知信号
MethodC();
Console.WriteLine();
Console.WriteLine("-----下面测试一边读,一边写-----");
TestReadWrite();
Console.ReadLine();
}
static void MethodA()
{
Console.WriteLine("开始执行方法A...");
Thread.Sleep(3000);
//Set(),其实就相当于一个开关,如果没有执行set()方法,下面的waitOne()就等不到让它执行的通知,这样一来waitOne后面的语句也不会执行了.
AutoResetEventA.Set();
Console.WriteLine("执行方法A完成...");
}
static void MethodB()
{
Console.WriteLine("开始执行方法B...");
Thread.Sleep(1000);
Console.WriteLine("执行方法B完成...");
//Set()函数的作用是 通知WaitOne()下面的代码开始执行,如果删除该Set()函数,造成MethodC()已经以下的代码将死循环等待执行
AutoResetEventB.Set();
}
static void MethodC()
{
Console.WriteLine("我是被WaitOne()阻塞执行的方法C,现在终于轮到我了。两个线程A、B都已经执行完成,正在执行方法C...");
}
/// <summary>
/// 读写分离的线程
/// </summary>
static void TestReadWrite()
{
int scanNumber = -1;
const int maxCount = 6;
Thread thread = new Thread(() =>
{
while (true)
{
//死循环等待Set()信号,收到Set()信号后方可写内容打印结果。
AutoResetEventReadWrite.WaitOne();
Console.WriteLine($"线程名称【{Thread.CurrentThread.Name}】,当前编号【{scanNumber}】");
}
});
thread.Name = "写线程Write";
thread.Start();
for (int i = 0; i < maxCount; i++)
{
scanNumber = i;
//Set()发信号,scanNumber值已经被写进去了。将通知一个WaitOne下面的代码执行
//注意:一个Set()只能通知一个WaitOne()下面的代码执行,不是多个WaitOne
AutoResetEventReadWrite.Set();
Thread.Sleep(1000);
}
Console.WriteLine("如果想要继续玩下去,请输入【Set {数字}】命令,比如输入 Set 1234");
string input = Console.ReadLine();
do
{
if (input.StartsWith("Set "))
{
int.TryParse(input.Substring("Set ".Length), out scanNumber);
AutoResetEventReadWrite.Set();
Thread.Sleep(1000);
}
Console.WriteLine("可继续输入【Set {数字}】命令 或者输入空格结束");
input = Console.ReadLine();
} while (input.Trim().Length > 0);
}
}
}