• .net core WebApi ManualResetEvent实现并发同步


    ManualResetEvent,即手动重置事件,通过信号量来判别当前线程是否应该阻塞或继续执行。使用方式与ManualResetEventSlim差不多,ManualResetEventSlim只是针对ManualResetEvent轻量化的使用。

    当执行ManualResetEvent.Set()时,ManualResetEvent会获得信号,并释放所有正在阻塞的线程

    当执行ManualResetEvent拥有信号时,执行WaitOne()时会立即释放当前线程,即当前线程会继续执行;若ManualResetEvent没有拥有信号时,执行WaitOne()时会被立即阻塞。

    一、在控制台应用程序中测试

    请看如下代码:

    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实现并发同步》这篇随笔。

  • 相关阅读:
    负载均衡器部署方式和工作原理
    Android 有关于* daemon not running.starting it now on port 5037 *ADB
    微信开发常用文档及参考资料
    XML解析之sax解析案例(二)使用sax解析把 xml文档封装成对象
    XML解析之sax解析案例(一)读取contact.xml文件,完整输出文档内容
    XML解析之SAX解析过程代码详解
    通过PHP current()函数获取未知字符键名数组第一个元素的值
    PHP检测链接是否是SSL连接 ,也就是判断HTTPS
    PHP反射ReflectionClass、ReflectionMethod 入门教程
    PHP 反射API说明
  • 原文地址:https://www.cnblogs.com/williamwsj/p/9722976.html
Copyright © 2020-2023  润新知