【.NET】多线程:自动重置事件与手动重置事件的区别
在多线程编程中,如果每个线程的运行不是完全独立的。那么,一个线程执行到某个时刻需要知道其他线程发生了什么。嗯,这就是所谓线程同步。同步事件对象(XXXEvent)有两种行为:1、等待。线程在此时会暂停运行,等待其他线程发出信号才继续(等你约);
2、发出信号。当前线程发出信号,其他正在等待线程收到信号后继续运行(我约你)。
从前,小明、小伟、小更、小红、小黄计划到野外去烤鱼吃。但他们只确定市郊东南方向的一片区域,并不能保证具体哪个地点适合烧烤。于是,他们商量好,大家同时从家里出发。小明离那里比较近,他先去考察一下;其他人到了东南郊后集合,等小明的消息。小明考察完毕,向大家群发消息说明选定的地点是F。最后大家继续前行,奔向F。
等待事件有好几个:
1、Mutex:互斥体。一次只能有一个线程获取到互斥体,其他线程只能等。占用互斥体的线程释放后,其他线程继续抢 Mutex。然后只有一个线程能抢到,其他线程继续等……
2、AutoResetEvent:自动事件,发出信号后立刻重置。
3、ManualResetEvent:手动事件,发出信号后不会立刻重置,得手动重置。
4、CountdownEvent:这个和上面两个差不多。但它会设定一个计数,线程发出信号时会减少计数。被阻止的线程要等到计数0) { // 取掉一个元素 char c = keyChars.Dequeue(); Console.WriteLine($"线程【{state}】获得字符:{c}"); } // 解锁 Monitor.Exit(keyChars); } }}这里我用泛型队列 Queue 来存放键盘敲入的字符,DoSomething 方法将放入线程池中运行。在从队列中取出元素并处理时,一定要记得上锁。我用的是 Monitor 对象的静态方法来上锁和解锁,当然你可以用 lock 语句块。
internal class Program
{
static AutoResetEvent theEvent = new(false);
static void Main(string[] args)
{
// 启动三个线程
ThreadPool.QueueUserWorkItem(DoWorking, "A");
ThreadPool.QueueUserWorkItem(DoWorking, "B");
ThreadPool.QueueUserWorkItem(DoWorking, "C");
// 主线程监听键盘消息
while(true)
{
var keyInfo = Console.ReadKey(true);
// 看看是不是Y键
if(keyInfo.Key == ConsoleKey.Y)
{
// 点亮信号
theEvent.Set();
}
// 输出一行,方便判断一个循环
Console.WriteLine("------------------------------");
}
}
static void DoWorking(object? state)
{
while(true)
{
// 等待主线程的信号
// 此线程会暂停
theEvent.WaitOne();
// 得到信号了,继续运行
Console.WriteLine("{0}已收到通知", state);
}
}
}如果不上锁,线程间在抢占资源时会导致不一致的状态。当A线程访问 keyChars.Count 属性时得到 1,还是 > 0 的,但在取出最后一个元素前,偏偏B线程动作快把最后一个元素拿走了。当A线程执行到 keyChars.Dequeue() 一句时,keyChars 队列中已经没有元素了,会发生错误。
主线程在 Enqueue 时并不需要锁定,因为元素送入队列只有一个线程在做,没人跟他抢资源,可以不锁定。
运行程序后,可以按字母、数字等按键来测试。毕竟像【F3】、【Ctrl】等按键获取到的是空白 char。
这样就顺畅很多了。
来源:https://www.cnblogs.com/tcjiaan/archive/2023/11/11/17826114.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页:
[1]