.net core WebApi Mutex實現並發同步


Mutex,中文譯為互斥體,在.net中也是作為一種線程或進程之間的互斥體存在。即在同一時刻,一個共享資源只允許被某一個線程或進程訪問,其他線程或進程需要等待(直至獲取互斥鎖為止)。

Mutex的使用方式與Monitor很相似,但絕不相同。Monitor支持線程間並發同步,Mutex不但支持線程也支持進程間並發同步。

Mutex有許多需要注意的地方,如果稍不注意,則你會被坑慘!

接下來我們使用控制台和webapi項目各自做測試。

一、使用控制台測試進程間的同步

測試代碼如下:

        [STAThread]
        static void Main(string[] args)
        {
            //使用指定名稱為MName的互斥體
            //如果獲取到名稱為MName的互斥體,則flag為true,否則為false。flag作用很大,如果是指定了互斥體的名稱,一定要用flag去判斷是否互斥。
            Mutex mutex = new Mutex(true, "MName", out bool flag);

            if (flag) {
                Console.WriteLine("1.獲取互斥鎖,開始運行");
                Thread.Sleep(5000);
                Console.WriteLine("2.釋放互斥鎖!");
                mutex.ReleaseMutex();
                Console.WriteLine("3.開始等待,5秒后超時!");
                if (mutex.WaitOne(5000))
                    Console.WriteLine("6.獲取互斥鎖,開始運行!");
                else Console.WriteLine("6.超時釋放互斥鎖!");
                Thread.Sleep(1000);               
            }
            else {
                Console.WriteLine("1.未獲取互斥鎖,開始等待!");
                mutex.WaitOne();
                Console.WriteLine("4.獲取互斥鎖,開始運行!");
                Console.WriteLine("5.釋放互斥鎖!");
                mutex.ReleaseMutex();               
            }

             Console.ReadKey();
        }

同時打開兩個程序的運行結果如下所示:

進程間的並發同步需要指定互斥體的名稱,只要是同一個名稱,它們就能互斥。

Mutex的WaitOne()方法是阻塞當前線程並等待獲取互斥體。ReleaseMutex()方法是釋放當前線程擁有的互斥體,如果當前進程或線程沒有擁有互斥體Mutex,則會拋出System.ApplicationException異常,這點需要注意。

WaitOne和ReleaseMutex方法最好成對出現,反正單個方法兩兩不相見。

多個ReleaseMutex方法一起運行的異常見以下圖片:

這個問題可以看ReleaseMutex方法的定義說明:

    /// <summary>Releases the <see cref="T:System.Threading.Mutex"></see> once.</summary>
    /// <exception cref="T:System.ApplicationException">The calling thread does not own the mutex.</exception>
    /// <exception cref="T:System.ObjectDisposedException">The current instance has already been disposed.</exception>
    public void ReleaseMutex();

產生System.ApplicationException異常的原因就是當前被調用的線程已經不擁有該互斥體!

  

二、使用WebApi項目測試線程間的同步

 測試代碼如下:

        [HttpGet("[controller]/v1/api/[action]")]
        public IActionResult Test() {            
            return Json(SynchronizationTest());
        }

        //指定一個互斥體名稱
        protected static string MutexName => "MName";
        public ResponseModel SynchronizationTest() {
            ResponseModel rc = new ResponseModel(0, "初始化");
            
            Mutex mutex = new Mutex(true, MutexName, out bool flag);
            try {
                //如果沒有獲取到互斥體,則等待,3秒后超時。如果獲取到了互斥體,則flag=true,否則flag=false。一定要做flag判斷,否則會拋出異常
                if(!flag)
                    mutex.WaitOne(3000);

                int count = RedisHelper.Get(GoodsNumberKey).ToInt32();
                if (count > 0) {
                    RedisHelper.Set(GoodsNumberKey, "-1");
                    rc.SetMessage("重置成功!");
                }
                else rc.SetMessage("已被重置,本次重置無效");
                
                rc.SetCode(Thread.CurrentThread.ManagedThreadId);
            }
            catch (Exception ex) {
                _log.Error(ex);
            }
            finally {
                mutex.ReleaseMutex();
                mutex.Close();
                mutex.Dispose();
            }

            return rc;
        }

接下來,我們使用如下代碼連續發送8次請求

            HttpHelper.GetOnStringAsync($"{Url}/Shop/v1/api/Test", contentType: "application/json").ContinueWith(it =>
            {
                Console.WriteLine(it.Result);
            });

測試的結果如下圖所示:

可以看見,我們連續發送8次請求,只有一次重置成功,其他的都因為互斥的關系而重置無效。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM