C#實(shí)現(xiàn)單線(xiàn)程異步互斥鎖的示例代碼
前言
C#對(duì)異步的支持越來(lái)越成熟,async、await簡(jiǎn)化了代碼也提高了可讀性,但由于在一段上下文中有了異步操作,意味著這段操作可能會(huì)被同時(shí)重復(fù)調(diào)用,如果本身沒(méi)有被設(shè)計(jì)可以重復(fù)調(diào)用的情況下,就很可能會(huì)出問(wèn)題。
一、異步互斥鎖的作用是什么
異步互斥鎖的作用是用于確保存在異步操作的上下文同步互斥。可以參考flutter的插件mutex功能與本文基本一樣。
示例一、創(chuàng)建和銷(xiāo)毀
有創(chuàng)建和銷(xiāo)毀兩個(gè)方法,兩個(gè)方法中都有異步操作,兩個(gè)方法可以單獨(dú)調(diào)用,但不可以同時(shí)調(diào)用。
單線(xiàn)程中連續(xù)調(diào)用創(chuàng)建和銷(xiāo)毀(不在同一個(gè)上下文無(wú)法用await),如果沒(méi)有互斥限制有可能出現(xiàn)如下的操作:
創(chuàng)建開(kāi)始->創(chuàng)建異步操作->消息隊(duì)列->銷(xiāo)毀開(kāi)始->銷(xiāo)毀異步操作->消息隊(duì)列->銷(xiāo)毀完成->消息隊(duì)列->創(chuàng)建完成
加入異步互斥鎖之后
加鎖->創(chuàng)建開(kāi)始->創(chuàng)建完成->解鎖
加鎖等待->銷(xiāo)毀開(kāi)始->銷(xiāo)毀完成->解鎖
二、如何實(shí)現(xiàn)
由于操作都是在單線(xiàn)程我們直接用標(biāo)識(shí)+隊(duì)列就可以實(shí)現(xiàn)一個(gè)互斥鎖。
1、標(biāo)識(shí)
(1)標(biāo)識(shí)是否鎖住
bool _lock = false;
(2)加鎖
_lock=true;
(3)解鎖
_lock=false;
2、異步通知
通過(guò)TaskCompletionSource可以實(shí)現(xiàn)異步通知
(1)創(chuàng)建對(duì)象
var tcs = new TaskCompletionSource();
(2)返回Task
return tcs.Task;
(3)通知完成
tcs.SetResult();
3、等待隊(duì)列
用一個(gè)隊(duì)列來(lái)記錄等待加鎖的請(qǐng)求。
(1)創(chuàng)建隊(duì)列
Queue<TaskCompletionSource> _queue = new Queue<TaskCompletionSource>();
(2) 等待加鎖
_queue.Enqueue(tcs);
(3)加鎖成功
_queue.Dequeue().SetResult();
三、完整代碼
/// <summary> /// 異步鎖,非線(xiàn)程鎖,只能用于單線(xiàn)程異步環(huán)境中。 /// </summary> class AsyncMutex { Queue<TaskCompletionSource> _queue = new Queue<TaskCompletionSource>(); bool _lock = false; /// <summary> /// 獲取鎖 /// </summary> /// <returns>返回Task,await后即進(jìn)入了鎖</returns> public Task Acquire() { if (_lock) { var tcs = new TaskCompletionSource(); _queue.Enqueue(tcs); return tcs.Task; } _lock = true; return Task.CompletedTask; } /// <summary> /// 嘗試獲取鎖 /// 因?yàn)槭菃尉€(xiàn)程環(huán)境,重復(fù)調(diào)用需要切換上下文,否則是無(wú)法成功的。 /// 比如可以await Task.Delay(30); /// </summary> /// <returns>是否成功</returns> public bool TryAcquire() { if (_lock) return false; return _lock = true; } /// <summary> /// 釋放鎖 /// </summary> public void Release() { if (_queue.Count > 0) { _queue.Dequeue().SetResult(); } else { _lock = false; } } }
四、使用示例
1、基本用法
直接加鎖
AsyncMutex _mtx = new AsyncMutex(); async void test() { await _mtx.Acquire(); //custom code _mtx.Release(); }
2、嘗試加鎖
加鎖成功才執(zhí)行操作
AsyncMutex _mtx = new AsyncMutex(); void test() { if (_mtx.TryAcquire()) { //custom code _mtx.Release(); } }
超時(shí)等待
AsyncMutex _mtx = new AsyncMutex(); async void test() { //超時(shí)等待300ms bool isLock = false; for (int i = 0; i < 10; i++) { if (isLock = _mtx.TryAcquire()) break; await Task.Delay(30); } if (isLock) { //custom code _mtx.Release(); } }
3、加鎖對(duì)比
(1)未加鎖
async void test(int num) { Console.WriteLine("enter " + num); //模擬異步操作 await Task.Delay(10); Console.WriteLine("exit " + num); } //.net 6.0 test(1); test(2); test(3);
可能出現(xiàn)的組合,效果預(yù)覽
(2)加鎖
AsyncMutex _mtx = new AsyncMutex(); async void test(int num) { await _mtx.Acquire(); Console.WriteLine("enter " + num); //模擬異步操作 await Task.Delay(10); Console.WriteLine("exit " + num); _mtx.Release(); } //.net 6.0 test(1); test(2); test(3);
效果預(yù)覽
總結(jié)
本文簡(jiǎn)單的實(shí)現(xiàn)了單線(xiàn)程的異步互斥鎖,實(shí)現(xiàn)起來(lái)相對(duì)簡(jiǎn)單,但作用還是比較大的。雖然說(shuō)有些情況的異步是可以在前期設(shè)計(jì)上避免同時(shí)調(diào)用,比如登錄按鈕點(diǎn)擊后出現(xiàn)蒙板不允許再次點(diǎn)擊,但是對(duì)于已存在的代碼出現(xiàn)了同時(shí)調(diào)用問(wèn)題,此時(shí)有互斥鎖則可以避免大范圍改動(dòng)代碼,有效解決問(wèn)題。
以上就是C#實(shí)現(xiàn)單線(xiàn)程異步互斥鎖的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于C#單線(xiàn)程異步互斥鎖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- C#實(shí)現(xiàn)凍結(jié)Excel窗口以鎖定行列或解除凍結(jié)
- C# lock線(xiàn)程鎖的用法
- C#常用多線(xiàn)程(線(xiàn)程同步,事件觸發(fā),信號(hào)量,互斥鎖,共享內(nèi)存,消息隊(duì)列)
- C#中使用CAS實(shí)現(xiàn)無(wú)鎖算法的示例詳解
- 基于C#解決庫(kù)存扣減及訂單創(chuàng)建時(shí)防止并發(fā)死鎖的問(wèn)題
- C#多線(xiàn)程之線(xiàn)程鎖
- C#多線(xiàn)程中的互斥鎖Mutex
- C#使用讀寫(xiě)鎖解決多線(xiàn)程并發(fā)問(wèn)題
- C#多線(xiàn)程系列之讀寫(xiě)鎖
- C#加鎖防止并發(fā)的幾種方法詳解
相關(guān)文章
C# DI依賴(lài)注入的實(shí)現(xiàn)示例
依賴(lài)注入是一種實(shí)現(xiàn)的方法,用于減少代碼之間的耦合,本文主要介紹了C# DI依賴(lài)注入的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣可以了解一下2023-12-12C#操作mysql數(shù)據(jù)庫(kù)的代碼實(shí)例
這篇文章為大家提供了一個(gè)C#操作mysql數(shù)據(jù)庫(kù)的實(shí)例,大家可以參考使用2013-11-11C# WebApi Get請(qǐng)求方式傳遞實(shí)體參數(shù)的方法示例
這篇文章主要給大家介紹了關(guān)于C# WebApi Get請(qǐng)求方式傳遞實(shí)體參數(shù)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用C#具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04用C#在本地創(chuàng)建一個(gè)Windows帳戶(hù)(DOS命令)
用C#在本地創(chuàng)建一個(gè)Windows帳戶(hù)(DOS命令)...2007-03-03C#中加載dll并調(diào)用其函數(shù)的實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇C#中加載dll并調(diào)用其函數(shù)的實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02C#不提升自己程序的權(quán)限實(shí)現(xiàn)操作注冊(cè)表
這篇文章主要介紹了C#不提升自己程序的權(quán)限實(shí)現(xiàn)操作注冊(cè)表的相關(guān)資料,需要的朋友可以參考下2022-12-12基于C#解決庫(kù)存扣減及訂單創(chuàng)建時(shí)防止并發(fā)死鎖的問(wèn)題
這篇文章主要介紹了基于C#解決庫(kù)存扣減及訂單創(chuàng)建時(shí)防止并發(fā)死鎖的問(wèn)題,很多開(kāi)發(fā)人員對(duì)于這個(gè)問(wèn)題的排查起來(lái)是比較困難的,而生產(chǎn)生的原因多種多樣,很多人認(rèn)是因?yàn)楸碇械臄?shù)據(jù)太多了同時(shí)操作的人多人才會(huì)產(chǎn)生這種錯(cuò)誤,下面我們來(lái)還原一下死鎖的過(guò)程2022-05-05