C#多線程編程中的鎖系統(tǒng)(三)
本章主要說下基于內(nèi)核模式構(gòu)造的線程同步方式,事件,信號量。
目錄
一:理論
二:WaitHandle
三:AutoResetEvent
四:ManualResetEvent
五:總結(jié)
一:理論
我們曉得線程同步可分為,用戶模式構(gòu)造和內(nèi)核模式構(gòu)造。
內(nèi)核模式構(gòu)造:是由windows系統(tǒng)本身使用,內(nèi)核對象進行調(diào)度協(xié)助的。內(nèi)核對象是系統(tǒng)地址空間中的一個內(nèi)存塊,由系統(tǒng)創(chuàng)建維護。
內(nèi)核對象為內(nèi)核所擁有,而不為進程所擁有,所以不同進程可以訪問同一個內(nèi)核對象, 如進程,線程,作業(yè),事件,文件,信號量,互斥量等都是內(nèi)核對象。
而信號量,互斥體,事件是windows專門用來幫助我們進行線程同步的內(nèi)核對象。
對于線程同步操作來說,內(nèi)核對象只有2個狀態(tài), 觸發(fā)(終止,true)、未觸發(fā)(非終止,false)。 未觸發(fā)不可調(diào)度,觸發(fā)可調(diào)度。
用戶模式構(gòu)造:是由特殊CPU指令來協(xié)調(diào)線程,上節(jié)講的volatile實現(xiàn)就是一種,Interlocked也是。 也可稱為非阻塞線程同步。
二:WaitHandle
在windows編程中,我們通過API創(chuàng)建一個內(nèi)核對象后會返回一個句柄,句柄則是每個進程句柄表的索引,而后可以拿到內(nèi)核對象的指針、掩碼、標示等。
而WaitHandle抽象基類類作用是包裝了一個windows內(nèi)核對象的句柄。我們來看下其中一個WaitOne的函數(shù)源碼(略精簡)。
public virtual bool WaitOne(TimeSpan timeout)
{
return WaitOne(timeout, false);
}
[System.Security.SecuritySafeCritical] // auto-generated
[SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread-safety.")]
private bool WaitOne(long timeout, bool exitContext)
{
return InternalWaitOne(safeWaitHandle, timeout, hasThreadAffinity, exitContext);
}
[System.Security.SecurityCritical]
internal static bool InternalWaitOne(SafeHandle waitableSafeHandle, long millisecondsTimeout, bool hasThreadAffinity, bool exitContext)
{
Contract.EndContractBlock();
int ret = WaitOneNative(waitableSafeHandle, (uint)millisecondsTimeout, hasThreadAffinity, exitContext);
if (ret == WAIT_ABANDONED)
{
ThrowAbandonedMutexException();
}
return (ret != WaitTimeout);
}
//調(diào)用win32 waitforsingleobjectEx
[System.Security.SecurityCritical]
[ResourceExposure(ResourceScope.None)]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private static extern int WaitOneNative(SafeHandle waitableSafeHandle, uint millisecondsTimeout, bool hasThreadAffinity, bool exitContext);
WaitAll 和WaitAny 調(diào)用win32中,waitformultipleobjectsEx函數(shù)。
SignalAndWaitOne 調(diào)用win32中,signalandwait函數(shù)。
調(diào)用api帶ex都是設(shè)置超時的。 如果我們在c#中不傳,默認是-1 表示無限期等待。
其中SafeWaitHandle字段,包含了一個win32內(nèi)核對象句柄。
理解了WaitHandle其他都好辦了,我們來看下它的派生類型。
WaitHandle
|——EventWaitHandle 事件構(gòu)造。
|——AutoResetEvent
|——ManualResetEvent
|——Semaphore 信號量構(gòu)造。
|——Mutex 互斥體構(gòu)造。
其中Semaphore和mutex第一章已經(jīng)說過了,下面來看看其他的。
三:AutoResetEvent
使用示例如下,有簡單注釋。 關(guān)于描述,盡量貼近系統(tǒng)自身術(shù)語。
static void Main(string[] args)
{
//AutoResetEvent example
//AutoResetEvent 通知正在等待的線程已發(fā)生的事件。
AutoResetEvent waitHandler = new AutoResetEvent(false);//false 即非終止,未觸發(fā)。
new Thread(() =>
{
waitHandler.WaitOne(); //阻塞當前線程,等待底層內(nèi)核對象收到信號。
Console.WriteLine("接收到信號,開始處理。");
}).Start();
new Thread(() =>
{
Thread.Sleep(2000);
Console.WriteLine("發(fā)信號");
waitHandler.Set(); //向內(nèi)核對象發(fā)送信號。設(shè)置事件對象為非終止狀態(tài)、false,解除阻塞。
}).Start();
//waitHandler.Close(); //釋放句柄資源。
//waitHandler.Reset(); //手動設(shè)置事件為非終止狀態(tài)、false,線程阻止。
Console.ReadLine();
}
WaitOne 阻塞線程,非自旋。
Set() 發(fā)出一個信號后,設(shè)置事件狀態(tài)為false。 這本應(yīng)該是2步的操作,AutoResetEvent.set()函數(shù),給2步一起自動做了,很方便。
四:ManualResetEvent
這個和上面基本一樣,從字面來說需要手動重置狀態(tài),我們來看例子。
ManualResetEvent manualWaitHandler = new ManualResetEvent(false);//false 即非終止,未觸發(fā)。
new Thread(() =>
{
manualWaitHandler.WaitOne(); //阻塞當前線程對象,等待信號。
Console.WriteLine("接收到信號,開始處理。");
manualWaitHandler.Reset(); //手動 設(shè)置事件對象狀態(tài)為非終止狀態(tài),false。
manualWaitHandler.WaitOne(); //這里直接阻塞等待無效,因為事件對象還是true,必須手動調(diào)reset。
Console.WriteLine("第二次接收到信號,開始處理。");
}).Start();
new Thread(() =>
{
Thread.Sleep(2000);
Console.WriteLine("發(fā)信號");
manualWaitHandler.Set(); //向事件對象發(fā)送ok信號。。
Thread.Sleep(2000);
Console.WriteLine("第二次發(fā)信號");
manualWaitHandler.Set();
}).Start();
Console.ReadLine();
這2則區(qū)別很小,其實是系統(tǒng)Api的區(qū)分,不是net類庫實現(xiàn)的。
在Win32Native類中,我可以看到KERNEL32 api 有這么個參數(shù)isManualReset。
[DllImport(KERNEL32, SetLastError=true, CharSet=CharSet.Auto, BestFitMapping=false)]
[ResourceExposure(ResourceScope.Machine)] // Machine or none based on the value of "name"
internal static extern SafeWaitHandle CreateEvent(SECURITY_ATTRIBUTES lpSecurityAttributes, bool isManualReset, bool initialState, String name);
五:總結(jié)
基于內(nèi)核模式構(gòu)造的同步步驟是: 托管代碼->用戶模式代碼->內(nèi)核模式代碼。
用戶模式構(gòu)造, 是利用CPU特殊指令,進行原子操作。
用戶模式代碼,如圖。 是指 托管代碼調(diào)用 win32代碼 這一層, 之后在調(diào)內(nèi)核模式代碼。
相關(guān)文章
.NET實現(xiàn):將EXE設(shè)置開機自動啟動
.NET實現(xiàn):將EXE設(shè)置開機自動啟動的方法,需要的朋友可以參考一下2013-03-03C#使用迭代器實現(xiàn)文字動態(tài)效果的示例代碼
這篇文章主要為大家詳細介紹了C#如何通過使用迭代器實現(xiàn)文字動態(tài)效果,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2024-02-02描述C#多線程中l(wèi)ock關(guān)鍵字的使用分析
本篇文章是對C#多線程中l(wèi)ock關(guān)鍵字的使用進行了詳細的分析介紹,需要的朋友參考下2013-06-06