<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
“mutex”是術語“互相排斥(mutually exclusive)”的簡寫形式,也就是互斥量。互斥量跟臨界區中提到的Monitor很相似,只有擁有互斥物件的執行緒才具有存取資源的許可權,由於互斥物件只有一個,因此就決定了任何情況下此共用資源都不會同時被多個執行緒所存取。當前佔據資源的執行緒在任務處理完後應將擁有的互斥物件交出,以便其他執行緒在獲得後得以存取資源。互斥量比臨界區複雜,因為使用互斥不僅僅能夠在同一應用程式不同執行緒中實現資源的安全共用,而且可以在不同應用程式的執行緒之間實現對資源的安全共用。
.Net中mutex由Mutex類
來表示。
先繞一小段路
在開始弄明白Mutex如何使用之前,我們要繞一小段路再回來。
讀書的時候,大家接觸互斥量、號誌這些玩意兒應該是在《作業系統》這一科。所以,其實這些玩意兒出現的原由是作為OS功能而存在。來看看Mutex的宣告:
[ComVisibleAttribute(true)] public sealed class Mutex : WaitHandle
於是我們不得不再走遠一些,看看WaitHandel的宣告:
[ComVisibleAttribute(true)] public abstract class WaitHandle : MarshalByRefObject, IDisposable
WaitHandle實現了一個介面,又繼承了一個父類別。看看它的父類別MarshalByRefObject:
允許在支援遠端處理的應用程式中跨應用程式域邊界存取物件。
備註:
應用程式域是一個作業系統程序中一個或多個應用程式所駐留的分割區。同一應用程式域中的物件直接通訊。不同應用程式域中的物件的通訊方式有兩種:一種是跨應用程式域邊界傳輸物件副本,一種是使用代理交換訊息。
MarshalByRefObject 是通過使用代理交換訊息來跨應用程式域邊界進行通訊的物件的基礎類別。
好啦,剩下的內容不用再看,否則就繞得太遠了。我們現在知道Mutex是WaitHandle的子類(偷偷地告訴你,以後要提到的EventWaitHandle、號誌Semaphore也是,而AutoResetEvent和ManualResetEvent則是它的孫子),而WaitHandle又繼承自具有在作業系統中跨越應用程式域邊界能力的MarshalByRefObject類。所以我們現在可以得到一些結論:
好了,終於繞回來了。來看看怎麼使用Mutex。
記得在try/finally結構中使用Mutex
。由於這兩個函數不等效於Monitor的Wait()和Pulse(),所以僅靠這ReleaseMutex()和WaitOne()兩個方法Mutex還無法適用於我們那個例子。
當然Mutext上還“算有”其它一些用於同步通知的方法,但它們都是其父類別WaitHandle上的靜態方法。因此它們並不是為Mutex特意“度身訂做”的,與Mutex使用的方式有些不搭調(你可以嘗試下用Mutex替換Monitor實現我們之前的場景看看),或者說Mutex其實是有些不情願的擁有這些方法。我們會在下一篇關於EventWaitHandle的Blog中再深入一些地討論Mutex和通知的問題。這裡暫且讓我們放一放,直接借用MSDN上的範例來簡單說明Mutex的最簡單的應用場景吧:
// This example shows how a Mutex is used to synchronize access // to a protected resource. Unlike Monitor, Mutex can be used with // WaitHandle.WaitAll and WaitAny, and can be passed across // AppDomain boundaries. using System; using System.Threading; class Test { // Create a new Mutex. The creating thread does not own the // Mutex. private static Mutex mut = new Mutex(); private const int numIterations = 1; private const int numThreads = 3; static void Main() { // Create the threads that will use the protected resource. for(int i = 0; i < numThreads; i++) { Thread myThread = new Thread(new ThreadStart(MyThreadProc)); myThread.Name = String.Format("Thread{0}", i + 1); myThread.Start(); } // The main thread exits, but the application continues to // run until all foreground threads have exited. } private static void MyThreadProc() { for(int i = 0; i < numIterations; i++) { UseResource(); } } // This method represents a resource that must be synchronized // so that only one thread at a time can enter. private static void UseResource() { // Wait until it is safe to enter. mut.WaitOne(); Console.WriteLine("{0} has entered the protected area", Thread.CurrentThread.Name); // Place code to access non-reentrant resources here. // Simulate some work. Thread.Sleep(500); Console.WriteLine("{0} is leaving the protected arearn", Thread.CurrentThread.Name); // Release the Mutex. mut.ReleaseMutex(); } }
雖然這只是一個示意性的範例,但是我仍然不得不因為這個範例中沒有使用try/finally來保證ReleaseMutex的執行而表示對微軟的鄙視。對於一個初學的人來說,第一個看到的例子可能會永遠影響這個人使用的習慣,所以是否在簡單示意的同時,也能“簡單地”給大家show一段足夠規範的程式碼?更何況有相當部分的人都是直接copy sample code……一邊告誡所有人Abandoned Mutexes的危害,一邊又給出一段一個異常就可以輕易引發這種錯誤的sample,MSDN不可細看。
我不得不說Mutex的作用於其說象Monitor不如說象lock,因為它只有等效於Monitro.Enter()/Exit()的作用,不同之處在於Mutex請求的鎖就是它自己。正因為如此,Mutex是可以也是必須(否則哪來的鎖?)被範例化的,而不象Monitor是個Static類,不能有自己的範例。
如果在一個應用程式域內使用Mutex,當然不如直接使用Monitor/lock更為合適,因為前面已經提到Mutex需要更大的開銷而執行較慢。不過Mutex畢竟不是Monitor/lock,它生來應用的場景就應該是用於程序間同步的。
除了在上面範例程式碼中沒有引數的建構函式外,Mutex還可以被其它的建構函式所建立:
區域性(Local)Mutex
。另外,這樣建立出的Mutex,建立者對這個範例並沒有擁有權,仍然需要呼叫WaitOne()去請求所有權。全域性(Global)Mutex
。如果String為null或者空字串,那麼這等同於建立一個未命名的Mutex。因為可能有其他程式先於你建立了同名的Mutex,因此返回的Mutex範例可能只是指向了同名的Mutex而已。但是,這個建構函式並沒有任何機制告訴我們這個情況。因此,如果要建立一個命名的Mutex,並且期望知道這個Mutex是否由你建立,最好使用下面兩個建構函式中的任意一個。最後,請注意name是大小寫敏感的
。另外,Mutex還有兩個過載的OpenExisting()方法可以開啟已經存在的Mutex。
如前所述,Mutex並不適合於有相互訊息通知的同步;另一方面而我們也多次提到區域性Mutex應該被Monitor/lock所取代;而跨應用程式的、相互訊息通知的同步由將在後面講到的EventWaiteHandle/AutoResetEvent/ManualResetEvent承擔更合適。所以,Mutex在.net中應用的場景似乎不多。不過,Mutex有個最常見的用途:用於控制一個應用程式只能有一個範例執行。
using System; using System.Threading; class MutexSample { private static Mutex mutex = null; //設為Static成員,是為了在整個程式生命週期內持有Mutex static void Main() { bool firstInstance; mutex = new Mutex(true, @"GlobalMutexSampleApp", out firstInstance); try { if (!firstInstance) { Console.WriteLine ("已有範例執行,輸入回車退出……"); Console.ReadLine(); return; } else { Console.WriteLine ("我們是第一個範例!"); for (int i=60; i > 0; --i) { Console.WriteLine (i); Thread.Sleep(1000); } } } finally { //只有第一個範例獲得控制權,因此只有在這種情況下才需要ReleaseMutex,否則會引發異常。 if (firstInstance) { mutex.ReleaseMutex(); } mutex.Close(); mutex = null; } } }
這是一個控制檯程式,你可以在編譯後嘗試一次執行多個程式,結果當然總是隻有一個程式在倒數計時。你可能會在網際網路上找到其它實現應用程式單例的方法,比如利用 Process 查詢程序名、利用Win32 API findwindow 查詢表單的方式等等,不過這些方法都不能保證絕對的單例。因為多程序和多執行緒是一樣的,由於CPU時間片隨機分配的原因,可能出現多個程序同時檢查到沒有其它範例執行的狀況。這點在CPU比較繁忙的情況下容易出現,現實的例子比如傲遊瀏覽器。即便你設定了只允許一個範例執行,當系統比較忙的時候,只要你嘗試多次開啟瀏覽器,那就有可能“幸運”的開啟若干獨立的瀏覽器視窗。
別忘了,要實現應用程式的單例,需要在在整個應用程式執行過程中都保持Mutex,而不只是在程式初始階段。所以,例子中Mutex的建立和銷燬程式碼包裹了整個Main()函數。
很奇怪,Mutex的父類別WaitHandle實現了IDisposable,但是我們在Mutex上卻找不到Dispose()方法,由於這個原因上面程式碼的finally中我們用的是Close()來釋放Mutex所佔用的資源。其實,這裡的Close()就等效於Dispose(),可這是為什麼?
再去看看WaitHandle,我們發現它實現的Disopose()方法是protected的,因此我們沒有辦法直接呼叫它。而它公開了一個Close()方法給呼叫者們用於替代Dispose(),因此Mutex上也就只有Close()。可這又是為什麼?
話說.Net最初的設計師是微軟從Borland公司挖過來的,也就是Delphi之父。熟悉Delphi的人都知道,Object Pascal構架中用於釋放資源的方法就是Dispose(),所以Dispose()也成為.Net構架中的重要的一員。
不過從語意上來講,對於檔案、網路連線之類的資源“Close”比“Dispose”更符合我們的習慣。因此“體貼”的微軟為了讓使用者(也就是我們這些寫程式碼的人)更“舒服”,在這種語意上更適合用Close的資源上,總是提供Close()作為Disopose()的公共實現。其實Close()內部不過是直接呼叫Dispose()而已。對於這種做法,我在感動之餘實在覺得有些多餘了,到底要把一個東西搞得多麼千變萬化才肯罷休?
如果你實在喜歡Dispose(),那麼可以用向上轉型 ((IDisposable)((WaitHandle)mutex)).Dispose()把它找出來。即強制把mutex轉換為WaitHandle,然後再把WaitHandle強制轉型為IDisposable,而IDisposable上的Dispose()是public的。不過我們終究並不確定Mutex以及WaitHandle的Close()中到底是不是在override的時候加入了什麼邏輯,所以還是老老實實用Close()好了~
到此這篇關於c#互斥鎖Mutex類用法介紹的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45