C#實(shí)現(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)題。
一、異步互斥鎖的作用是什么
異步互斥鎖的作用是用于確保存在異步操作的上下文同步互斥??梢詤⒖糵lutter的插件mutex功能與本文基本一樣。
示例一、創(chuàng)建和銷(xiāo)毀
有創(chuàng)建和銷(xiāo)毀兩個(gè)方法,兩個(gè)方法中都有異步操作,兩個(gè)方法可以單獨(dú)調(diào)用,但不可以同時(shí)調(diào)用。
單線程中連續(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)
由于操作都是在單線程我們直接用標(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>
/// 異步鎖,非線程鎖,只能用于單線程異步環(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)槭菃尉€程環(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)了單線程的異步互斥鎖,實(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)單線程異步互斥鎖的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于C#單線程異步互斥鎖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C# OpenVINO實(shí)現(xiàn)圖片旋轉(zhuǎn)角度檢測(cè)
這篇文章主要為大家詳細(xì)介紹了C#?OpenVINO如何實(shí)現(xiàn)圖片旋轉(zhuǎn)角度檢測(cè),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-02-02
C#實(shí)現(xiàn)簡(jiǎn)單的計(jì)算器小程序
這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)簡(jiǎn)單的計(jì)算器小程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
C#使用GDI+實(shí)現(xiàn)生成驗(yàn)證碼
這篇文章介紹了C#使用GDI+實(shí)現(xiàn)生成驗(yàn)證碼的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05
C#結(jié)合Free?Spire.PDF實(shí)現(xiàn)刪除PDF中的數(shù)字簽名
這篇文章主要為大家詳細(xì)介紹了C#如何結(jié)合Free?Spire.PDF實(shí)現(xiàn)刪除PDF中的數(shù)字簽名,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以參考一下2026-03-03
C#檢測(cè)pc光驅(qū)里是否插入了光盤(pán)的方法
這篇文章主要介紹了C#檢測(cè)pc光驅(qū)里是否插入了光盤(pán)的方法,涉及C#針對(duì)光驅(qū)等硬件檢測(cè)操作的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07
在C#中創(chuàng)建和讀取XML文件的實(shí)現(xiàn)方法
項(xiàng)目中需要將前臺(tái)頁(yè)面中的信息保存下來(lái)并存儲(chǔ)為xml文件格式到數(shù)據(jù)庫(kù)中去。因此我先在這里通過(guò)一個(gè)小實(shí)例來(lái)學(xué)習(xí)xml的創(chuàng)建與讀取2013-09-09
C#使用SemaphoreSlim進(jìn)行并發(fā)控制的最佳實(shí)踐
本文介紹了在C#中控制異步并發(fā)的標(biāo)準(zhǔn)解決方案SemaphoreSlim,,首先分析了不加控制的并發(fā)可能導(dǎo)致的問(wèn)題和錯(cuò)誤解決方案,接著詳細(xì)介紹了SemaphoreSlim的工作原理及其在生產(chǎn)環(huán)境中的使用方法,需要的朋友可以參考下2026-04-04
C#預(yù)處理指令之#line,#pragma warning 詳細(xì)解析
#line 指令可能由生成過(guò)程中的自動(dòng)中間步驟使用。例如,如果行從原始的源代碼文件中移除,但是您仍希望編譯器基于文件中的原始行號(hào)生成輸出,則可以移除行,然后用 #line 模擬原始行號(hào)2014-01-01

