世界都在背叛我 发表于 2023-11-11 21:38:48

【.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]
查看完整版本: 【.NET】多线程:自动重置事件与手动重置事件的区别