ManualResetEvent,即手动重置事件,通过信号量来判别当前线程是否应该阻塞或继续执行。使用方式与ManualResetEventSlim差不多,ManualResetEventSlim只是针对ManualResetEvent轻量化的使用。
一、在控制台应用程序中测试
请看如下代码:
private static ManualResetEvent mre = new ManualResetEvent(true); static void Main(string[] args) { //释放信号量 mre.Reset(); for (int i = 0; i < 3; i++) { Thread t = new Thread(ManualSubFuncTest); t.Name = $"Thread.{i}"; t.Start(); } Thread.SpinWait(10); Console.WriteLine("all threads have started,please waiting for 3 seconds..."); Thread.Sleep(3000); //获取信号量 mre.Set(); Console.ReadKey(); } static void ManualSubFuncTest() { string name = Thread.CurrentThread.Name; Console.WriteLine($"{name} starts and calling waitone() method " ); //如果mre不拥有信号量,则等待,否则继续执行 mre.WaitOne(); Console.WriteLine($"{name} ends"); }
结果如下图所示:
测试结果和预期一样。
二、在webapi项目中测试
代码如下:
[HttpGet("[controller]/v1/api/[action]")] public IActionResult Test() { return Json(SynchronizationTest()); } protected static int Counter = 1;//1:空闲 0:非空闲 protected static ManualResetEvent Mre=new ManualResetEvent(false); public ResponseModel SynchronizationTest() { ResponseModel rc = new ResponseModel(0, "初始化"); try { Mre.Reset(); //如果其他线程正在操作,则等待,5秒后超时 if (Interlocked.CompareExchange(ref Counter, 0, 1) == 0) Mre.WaitOne(3000); int count = RedisHelper.Get(GoodsNumberKey).ToInt32(); if (count > 0) { RedisHelper.Set(GoodsNumberKey, "-1"); rc.SetMessage("重置成功!"); } else rc.SetMessage("已被重置,本次重置无效"); } catch (Exception ex) { _log.Error(ex); } finally { //转为空闲状态 Interlocked.Exchange(ref Counter, 1); //设置信号量,让上面的 Mre.Wait(3000);取消等待,继续执行代码 Mre.Set(); } return rc; }
接下来,我们连续发送8次请求,看看结果如何:
由结果可以看到,多个线程中,只有一个线程操作成功,起到了并发同步的目的。
注意:信号量事件(ManualResetEvent)对象要用同一个的WaitOne、Reset和Set配合才会实现并发同步的效果。
如果是轻量化的线程间同步操作,建议用ManualResetEventSlim。其效果和ManualResetEvent是一样的,可以去看看我的《.net core WebApi Interlocked配合ManualResetEventSlim实现并发同步》这篇随笔。
原文地址:https://www.cnblogs.com/williamwsj/p/9722976.html
时间: 2024-10-12 07:30:35