C#之線程同步Mutex類方式
Mutex(互斥鎖)
Mutex: 命名空間:System.Threading
一個(gè)同步基元,也可用于進(jìn)程間同步。是.NET Framework 中提供跨多個(gè)線程,或多個(gè)進(jìn)程同步訪問的一個(gè)類。它非常類似于Monitor類,他們都只有一個(gè)線程能擁有鎖定,只有一個(gè)線程能獲得互斥鎖,訪問受互斥保護(hù)的同步代碼區(qū)域。
在 Mutex 類的構(gòu)造函數(shù)中,可以指定互斥是否最初應(yīng)有主線程擁有,定義互斥的名稱,獲得互斥是否以存在的信息。
例:
bool flag; Mutex mutex = new Mutex(false, "ProcSharpMutex", out flag);
第三個(gè)參數(shù)定義輸出參數(shù),接受一個(gè)互斥是否為新建的 bool 值。如果返回 False 表示互斥已經(jīng)定義?;コ饪梢栽诹硪粋€(gè)進(jìn)程中定義,因?yàn)椴僮飨到y(tǒng)能夠識(shí)別有名子的互斥,它有不同的進(jìn)程共享。
如果沒有給互斥指定名稱,互斥就是未命名的,不在不同的進(jìn)程之間共享。
要打開已有的互斥,還可以使用 Mutex.OpenExisting()方法,它不需要用構(gòu)造函數(shù)創(chuàng)建互斥時(shí)需要的相同 .NET權(quán)限。
由于 Mutex 派生于 WaitHandle , 因此可以利用 WaitOne() 方法獲得互斥鎖定,在該過程中成為該互斥擁有者。調(diào)用 ReleaseMutex() 方法 ,即可釋放互斥。
例:
if (mutex.WaitOne()) { try { //同步的區(qū)域 } finally { mutex.ReleaseMutex();//釋放 mutex 一次 } } else { //等待時(shí)發(fā)生了一些問題 }
構(gòu)造函數(shù)
構(gòu)造函數(shù) | 描述 |
---|---|
Mutex() | 使用默認(rèn)屬性初始化 Mutex 類的新實(shí)例。 |
Mutex(Boolean) | 用一個(gè)指示調(diào)用線程是否應(yīng)擁有互斥體的初始所屬權(quán)的布爾值來初始化 Mutex 類的新實(shí)例。 |
Mutex(Boolean, String) | 用一個(gè)指示調(diào)用線程是否應(yīng)擁有互斥體的初始所屬權(quán)的布爾值和一個(gè)作為互斥體名稱的字符串來初始化 Mutex 類的新實(shí)例。 |
Mutex(Boolean, String, Boolean) | 使用可指示調(diào)用線程是否應(yīng)具有互斥體的初始所有權(quán)以及字符串是否為互斥體的名稱的 Boolean 值和當(dāng)線程返回時(shí)可指示調(diào)用線程是否已賦予互斥體的初始所有權(quán)的 Boolean 值初始化 Mutex 類的新實(shí)例。 |
Mutex(Boolean, String, Boolean, MutexSecurity) | 使用可指示調(diào)用線程是否應(yīng)具有互斥體的初始所有權(quán)以及字符串是否為互斥體的名稱的 Boolean 值和當(dāng)線程返回時(shí)可指示調(diào)用線程是否已賦予互斥體的初始所有權(quán)以及訪問控制安全是否已應(yīng)用到命名互斥體的 Boolean 變量初始化 Mutex 類的新實(shí)例。 |
方法
方法名稱 | 描述 |
---|---|
Close() | 釋放由當(dāng)前 WaitHandle 占用的所有資源。 (Inherited from WaitHandle) |
CreateObjRef(Type) | 創(chuàng)建一個(gè)對(duì)象,該對(duì)象包含生成用于與遠(yuǎn)程對(duì)象進(jìn)行通信的代理所需的全部相關(guān)信息。 (Inherited from MarshalByRefObject) |
Dispose() | 釋放 WaitHandle 類的當(dāng)前實(shí)例所使用的所有資源。 (Inherited from WaitHandle) |
Dispose(Boolean) | 當(dāng)在派生類中重寫時(shí),釋放 WaitHandle 使用的非托管資源,并且可選擇釋放托管資源。 (Inherited from WaitHandle) |
Equals(Object) | 確定指定的對(duì)象是否等于當(dāng)前對(duì)象。 (Inherited from Object) |
GetAccessControl() | 獲取一個(gè) MutexSecurity 對(duì)象,該對(duì)象表示已命名互斥體的訪問控制安全性。 |
OpenExisting(String) | 打開指定名稱為 mutex(如果已經(jīng)存在)。 |
OpenExisting(String, MutexRights) | 用安全訪問權(quán)限打開指定名稱為 mutex(如果已經(jīng)存在),并返回指示操作是否成功的值。 |
ReleaseMutex() | 釋放 Mutex 一次。 |
WaitOne() | 阻止當(dāng)前線程,直到當(dāng)前 WaitHandle 收到信號(hào)。 |
WaitOne(Int32) | 阻止當(dāng)前線程,直到當(dāng)前 WaitHandle 收到信號(hào),同時(shí)使用 32 位帶符號(hào)整數(shù)指定時(shí)間間隔(以毫秒為單位)。 |
屬性
屬性 | 描述 |
---|---|
Handle | 獲取或設(shè)置本機(jī)操作系統(tǒng)句柄。 |
SafeWaitHandle | 獲取或設(shè)置本機(jī)操作系統(tǒng)句柄。 |
字段
字段 | 描述 |
---|---|
WaitTimeout | 指示在任何等待句柄終止之前 WaitAny(WaitHandle[], Int32, Boolean) 操作已超時(shí)。 此字段為常數(shù)。 |
詳細(xì)結(jié)構(gòu)請(qǐng)點(diǎn)擊MSDN 文檔連接: Mutex
基本用法Dome1:
// 創(chuàng)建一個(gè)新的互斥對(duì)象。創(chuàng)建線程不擁有互斥對(duì)象。 private static Mutex mut = new Mutex(); private const int numIterations = 1; private const int numThreads = 3; static void Main(string[] args) { // 創(chuàng)建將使用受保護(hù)資源的3個(gè)線程。 for (int i = 0; i < numThreads; i++) { Thread newThread = new Thread(new ThreadStart(ThreadProc)); newThread.Name = String.Format("Thread{0}", i + 1); newThread.Start(); } //主線程退出,但是應(yīng)用程序繼續(xù)運(yùn)行,直到所有前臺(tái)線程退出為止。 Console.ReadKey(); } private static void ThreadProc() { for (int i = 0; i < numIterations; i++) { UseResource(); } } //此方法表示必須同步的資源 //這樣每次只能進(jìn)入一個(gè)線程。 private static void UseResource() { // 等安全了再進(jìn)去。 Console.WriteLine("{0} 正在請(qǐng)求互斥鎖", Thread.CurrentThread.Name); mut.WaitOne(); Console.WriteLine("{0} 已進(jìn)入保護(hù)區(qū)", Thread.CurrentThread.Name); //在這里放置訪問不可重入資源的代碼。 // 模擬一些工作。 Thread.Sleep(500); Console.WriteLine("{0} 離開保護(hù)區(qū)", Thread.CurrentThread.Name); // 釋放互斥鎖。 mut.ReleaseMutex(); Console.WriteLine("{0} 正在釋放互斥鎖", Thread.CurrentThread.Name); }
輸出:每次執(zhí)行進(jìn)入的先后順序都不一樣
Dome2: 每個(gè)線程調(diào)用WaitOne(Int32)方法以獲取互斥體。 如果達(dá)到超時(shí)間隔,該方法返回false,并在線程獲取 mutex 既不獲得互斥鎖保護(hù)的資源訪問權(quán)。 ReleaseMutex只能由獲得該互斥體的線程調(diào)用方法。
// 創(chuàng)建一個(gè)新的互斥對(duì)象。創(chuàng)建線程不擁有互斥對(duì)象。 private static Mutex mut = new Mutex(); private const int numIterations = 1; private const int numThreads = 3; static void Main(string[] args) { Program p = new Program(); p.StartThreads(); Console.ReadKey();//這里阻塞一下,可以看到輸出結(jié)果 } private void StartThreads() { // 創(chuàng)建將使用受保護(hù)資源的3個(gè)線程。 for (int i = 0; i < numThreads; i++) { Thread newThread = new Thread(new ThreadStart(ThreadProc)); newThread.Name = String.Format("Thread{0}", i + 1); newThread.Start(); } //主線程退出,但是應(yīng)用程序繼續(xù)運(yùn)行,直到所有前臺(tái)線程退出為止。 } private static void ThreadProc() { for (int i = 0; i < numIterations; i++) { UseResource(); } } //此方法表示必須同步的資源 //這樣每次只能進(jìn)入一個(gè)線程。 private static void UseResource() { // 等安全了再進(jìn)去。 Console.WriteLine("{0} 正在請(qǐng)求互斥鎖", Thread.CurrentThread.Name); if(mut.WaitOne(1000)) { Console.WriteLine("{0} 已進(jìn)入保護(hù)區(qū)", Thread.CurrentThread.Name); //在這里放置訪問不可重入資源的代碼。 // 模擬一些工作。 Thread.Sleep(500); Console.WriteLine("{0} 離開保護(hù)區(qū)", Thread.CurrentThread.Name); // 釋放互斥鎖。 mut.ReleaseMutex(); Console.WriteLine("{0} 正在釋放互斥鎖", Thread.CurrentThread.Name); } else { Console.WriteLine("{0} 沒有獲取互斥鎖", Thread.CurrentThread.Name); } } ~Program() { mut.Dispose(); }
輸出結(jié)果:
重要
此類型實(shí)現(xiàn)IDisposable接口。 在使用完類型后,您應(yīng)直接或間接釋放類型。 若要直接釋放類型,調(diào)用其Dispose中的方法 try / catch塊。 若要間接釋放類型,請(qǐng)使用 using(在 C# 中)或 Using
可以使用WaitHandle.WaitOne來請(qǐng)求所有權(quán)的互斥體的方法。 調(diào)用線程受到阻止,直到發(fā)生下列情況之一:
- 互斥體是發(fā)出信號(hào),以指示它不屬于。 在此情況下,WaitOne方法將返回true,調(diào)用線程是互斥體的所有權(quán),并訪問受該互斥體的資源。 完成后訪問資源,必須調(diào)用線程ReleaseMutex方法來釋放 mutex 的所有權(quán)。 示例部分中的第一個(gè)示例說明了此模式。
- 對(duì)的調(diào)用中指定的超時(shí)間隔WaitOne具有方法millisecondsTimeout或timeout參數(shù)已過。 在此情況下,WaitOne方法將返回false,并調(diào)用線程不會(huì)進(jìn)一步嘗試獲取 mutex 的所有權(quán)。 在這種情況下,您應(yīng)構(gòu)建你的代碼,以便由 mutex 保護(hù)資源的訪問權(quán)限被拒絕對(duì)調(diào)用線程。 線程永遠(yuǎn)不會(huì)獲取 mutex 的所有權(quán),因?yàn)樗荒苷{(diào)用ReleaseMutex方法。 示例部分中的第二個(gè)示例說明了此模式。
Mutex類強(qiáng)制線程標(biāo)識(shí),因此只能由獲得它的線程可以釋放互斥體。 與此相反,Semaphore類不會(huì)強(qiáng)制線程標(biāo)識(shí)。 此外可以跨應(yīng)用程序域邊界傳遞了互斥體。
擁有 mutex 的線程可以請(qǐng)求中重復(fù)調(diào)用相同的互斥體WaitOne而不會(huì)阻止其執(zhí)行。 但是,調(diào)用線程必須ReleaseMutex方法相同數(shù)量的次數(shù)與釋放 mutex 的所有權(quán)。
因?yàn)镸utex類繼承自WaitHandle,你還可以調(diào)用靜態(tài)WaitHandle.WaitAll和WaitHandle.WaitAny方法來同步受保護(hù)資源的訪問權(quán)限。
如果線程終止時(shí)擁有互斥體,則認(rèn)為該 mutex 已放棄。 互斥體的狀態(tài)設(shè)置為終止?fàn)顟B(tài),并且下一個(gè)等待線程獲取所有權(quán)。 從.NET Framework 的版本 2.0AbandonedMutexException獲取已放棄的互斥體的下一個(gè)線程中引發(fā)。 在.NET Framework 2.0 版中之前, 未不引發(fā)任何異常。
注意
放棄的 mutex 通常表明代碼中的存在嚴(yán)重錯(cuò)誤。 線程退出時(shí)不釋放互斥體,由 mutex 保護(hù)的數(shù)據(jù)結(jié)構(gòu)不可能處于一致狀態(tài)。 為請(qǐng)求 mutex 所有權(quán)的下一個(gè)線程可以處理此異常并繼續(xù)操作,如果可以驗(yàn)證數(shù)據(jù)結(jié)構(gòu)的完整性。
對(duì)于系統(tǒng)范圍的 mutex,放棄的 mutex 可能指示應(yīng)用程序已突然終止(例如,通過使用 Windows 任務(wù)管理器終止)。
Mutex 有兩種類型: 本地 mutex,未命名,并命名系統(tǒng) mutex。 本地 mutex 僅存在于進(jìn)程中。 可由您具有對(duì)引用的過程中的任何線程Mutex表示該互斥體的對(duì)象。 每個(gè)未命名Mutex對(duì)象表示單獨(dú)的本地 mutex。
已命名的系統(tǒng)互斥體,顯示整個(gè)操作系統(tǒng),可用于同步進(jìn)程的活動(dòng)。 您可以創(chuàng)建Mutex使用接受名稱的構(gòu)造函數(shù)表示命名的系統(tǒng)互斥體對(duì)象。 可以在同一時(shí)間創(chuàng)建操作系統(tǒng)對(duì)象也可以在創(chuàng)建之前存在Mutex對(duì)象。 可以創(chuàng)建多個(gè)表示同一命名系統(tǒng) mutex 的 Mutex 對(duì)象,還能使用 OpenExisting 方法打開現(xiàn)有的命名系統(tǒng) mutex。
備注:
在運(yùn)行終端服務(wù)的服務(wù)器,命名的系統(tǒng)互斥體可以包含兩個(gè)級(jí)別的可見性。 如果其名稱以前綴開頭"Global",互斥體是在所有終端服務(wù)器會(huì)話中可見。 如果其名稱以前綴開頭"本地",互斥體是僅在終端服務(wù)器會(huì)話中可見的創(chuàng)建位置。 在這種情況下,具有相同名稱單獨(dú)的互斥鎖可以存在于每個(gè)服務(wù)器上的其他終端服務(wù)器會(huì)話。 如果您不指定前綴創(chuàng)建已命名的互斥體時(shí),它將前綴"本地"。 終端服務(wù)器會(huì)話中,只能由其前綴的名稱不同的兩個(gè) mutex 有單獨(dú)的互斥體,且都在終端服務(wù)器會(huì)話中對(duì)所有進(jìn)程可見。 也就是說,前綴名稱"Global"和"本地"描述相對(duì)于終端服務(wù)器會(huì)話、 不相對(duì)于進(jìn)程的互斥體名稱的作用域。
反斜杠 () 是互斥體名稱中的保留字符。 請(qǐng)勿在互斥體名稱中使用反斜杠 (),除非在終端服務(wù)器會(huì)話中使用互斥體的注釋中另有規(guī)定。 否則,即使互斥體的名稱表示現(xiàn)有文件,也可能引發(fā) DirectoryNotFoundException。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
C# BinaryReader實(shí)現(xiàn)讀取二進(jìn)制文件
在 C# 以二進(jìn)制形式讀取數(shù)據(jù)時(shí)使用的是 BinaryReader 類。本文介紹了C# BinaryReader實(shí)現(xiàn)讀取二進(jìn)制文件,感興趣的可以了解一下2021-06-06Unity ScrollRect實(shí)現(xiàn)軌跡滑動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了Unity ScrollRect實(shí)現(xiàn)軌跡滑動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09基于Unity3D實(shí)現(xiàn)仿真時(shí)鐘詳解
這篇文章主要為大家詳細(xì)介紹了如何利用Unity3D模擬實(shí)現(xiàn)一個(gè)簡(jiǎn)單是時(shí)鐘效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-01-01C#中GraphicsPath的AddString方法用法實(shí)例
這篇文章主要介紹了C#中GraphicsPath的AddString方法用法,實(shí)例分析了AddString方法添加字符串的相關(guān)使用技巧,需要的朋友可以參考下2015-06-06C#批量插入數(shù)據(jù)到Sqlserver中的三種方式
這篇文章主要為大家詳細(xì)介紹了C#批量插入數(shù)據(jù)到Sqlserver中的三種方式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12C#中Override關(guān)鍵字和New關(guān)鍵字的用法詳解
這篇文章主要介紹了C#中Override關(guān)鍵字和New關(guān)鍵字的用法,需要的朋友可以參考下2016-01-01WCF基礎(chǔ)介紹并創(chuàng)建簡(jiǎn)單應(yīng)用程序
這篇文章介紹了WCF基礎(chǔ)并創(chuàng)建簡(jiǎn)單WCF應(yīng)用程序,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-01-01