本隨筆續接:.NET 同步與非同步之線程安全的集合(十一)
本隨筆及接下來的兩篇隨筆,將介紹.NET 同步與非同步系列的最後一個大塊知識點:WaitHandle家族。
抽象基底類別:WaitHandle, 三個子類別: EventWaitHandle(Event通知) 、Mutex(進程同步鎖定)、Semaphone (信號量),還有兩個孫子輩:System.Threading.AutoResetEvent、System. Threading.ManualResetEvent,都是 EventWaitHandle 的子類別。
[ComVisibleAttribute(true)]public abstract class WaitHandle : MarshalByRefObject, IDisposable
透過上面的訊息,我們可以知道 WaitHandle 繼承自 MarshalByRefObject, 並實作了 IDisposable 介面。
對於 MarshalByRefObject ,你也許不是很熟悉,但它的很多子類你一定會用過的,讓我們來揭開它的廬山真面目。
在MSND中是這樣描述 MarshalByRefObject 的:
應用程式域是一個作業系統進程中一個或多個應用程式所駐留的分割區。同一應用程式域中的物件直接通訊。不同應用程式域中的物件的通訊方式有兩種:一種是跨應用程式域邊界傳輸物件副本,一種是使用代理交換訊息。 MarshalByRefObject 是透過使用代理交換訊息來跨應用程式域邊界進行通訊的物件的基底類別。
看到這裡你也許更困惑了,我用過它? 用過它的子類別? 沒錯,就是用過它的子類,而且還很多。
例如System.Drawing命名空間的Brush、Image、Pen、Font 等等,還有個大家更熟悉的System.IO命名空間下的Stream.
延展閱讀:利用 MarshalByRefObject 實作AOP 。
看到這裡我們只要知道 WaitHandle 具有跨應用程式網域進行通訊的能力就可以了。
反觀Monitor、平常只用來在應用程式域內的執行緒之間通訊。其實,如果用於鎖的物件衍生自MarshalByRefObject,Monitor 也可在多個應用程式網域中提供鎖定。
Mutex由於需要呼叫作業系統資源,因此執行的開銷比Monitor大得多,所以如果僅需要在應用程式內部的執行緒間同步操作,Monitor/lock應是首選
WaitOne() /WaitOne(TimeSpan, Boolean)及若干個重載:請求所有權,該調用會一直阻塞到當前mutex 收到信號,或直至達到可選的超時間隔,這幾個方法都不需要提供鎖定物件作為額外參數。
您可以使用 WaitHandle.WaitOne 請求所有權的互斥體的方法。 呼叫執行緒被阻止,直到發生下列情況之一︰
#互斥體發出訊號以指示不擁有。 在此情況下, WaitOne 方法將傳回 true, ,呼叫執行緒的互斥體所有權,並存取由 mutex 保護的資源。 執行緒完成後存取資源,必須呼叫 ReleaseMutex 方法來釋放 mutex 的所有權。
#對呼叫中指定的逾時間隔 WaitOne 具有方法 millisecondsTimeout 或 timeout 參數已過。 在此情況下, WaitOne 方法將傳回 false, 此時該執行緒不會取得互斥體的所有權。
ReleaseMutex():釋放目前 Mutex 一次。請注意,這裡強調了一次,因為擁有互斥體的線程可以在重複的調用WaitOne系列函數而不會阻止其執行;這個跟Monitor的Enter()/Exit()可以在獲取物件鎖定後可以被重複調用一樣。 Mutex被呼叫的次數由公共語言運行庫(CLR)保存,每WaitOne()一次計數+1,每ReleaseMutex()一次計數-1,只要這個計數不為0,其它Mutex的等待者就會認為這個Mutex沒有被釋放,也就沒有辦法獲得該Mutex。 另外,跟Monitor.Exit()一樣,只有Mutex的擁有者才能RleaseMutex(),否則會引發異常。
如果執行緒在擁有互斥體時終止,我們稱此互斥體被遺棄(Abandoned)。在MSDN裡,微軟以警告的方式指出這屬於「嚴重的」程式錯誤。這是說擁有mutex的擁有者在獲得所有權後,WaitOne()和RelaseMutex()的次數不對等,呼叫者本身又不負責任地中止,造成mutex 正在保護的資源可能會處於不一致的狀態。其實,這只不過是提醒你記得在try/finally結構中使用Mutex。
如果在一个应用程序域内使用Mutex,当然不如直接使用Monitor/lock更为合适,因为前面已经提到Mutex需要更大的开销而执行较慢。不过Mutex毕竟不是Monitor/lock,它生来应用的场景就应该是用于进程间同步的。用于在进程间通讯的Mutex我们称为全局Mutex,而只用于在应用程序域内部通讯的Mutex、我们称为局部Mutex.
全局Mutex和局部Mutex是通过构造函数来构造不同的实例的,让我们来看一下Mutex的构造函数,一共有5个,挑两个具有代表性的看一下吧:
Mutex():用无参数的构造函数得到的Mutex没有任何名称,而进程间无法通过变量的形式共享数据,所以没有名称的Mutex也叫做局部(Local)Mutex。另外,这样创建出的Mutex,创建者对这个实例并没有拥有权,仍然需要调用WaitOne()去请求所有权。
Mutex(Boolean initiallyOwned, String name, out Booldan createdNew, MutexSecurity):第一个bool参数:指示初始化的实例是否拥有互斥体所有权。第二个string类型、为互斥体指定一个名称,如果string为null或者空字符串 则相当于创建一个没有名字的Mutex,当属于局部Mutex. 而有名字的Mutex当属于全局Mutex.第三个bool参数、如果已经初始化了互斥体 返回True, 如果互斥体已经存在则返回False. 最后一个参数用于Mutex访问的安全性控制。
Mutex天生为进程间的同步基元,因此它可以用来控制应用程序的单实例:
/// <summary>/// 单实例运行/// </summary>/// <returns> true 应用程序已启动,false 则没有 </returns>public bool SingleRun(ref System.Threading.Mutex mutex ) { mutex = new System.Threading.Mutex(false, "WINDOWS"); if (!mutex.WaitOne(0, false)) { mutex.Close(); mutex = null; } if (mutex == null) { return true; } return false; }
以上是詳解.NET 同步與非同步 之 Mutex的詳細內容。更多資訊請關注PHP中文網其他相關文章!