本章主要說下基于內核模式構造的線程同步方式,事件,信號量。
目錄
一:理論
二:WaitHandle
三:AutoResetEvent
四:ManualResetEvent
五:總結
一:理論
我們曉得線程同步可分為,用戶模式構造和內核模式構造。
內核模式構造:是由windows系統本身使用,內核對象進行調度協助的。內核對象是系統地址空間中的一個內存塊,由系統創建維護。
內核對象為內核所擁有,而不為進程所擁有,所以不同進程可以訪問同一個內核對象, 如進程,線程,作業,事件,文件,信號量,互斥量等都是內核對象。
而信號量,互斥體,事件是windows專門用來幫助我們進行線程同步的內核對象。
對于線程同步操作來說,內核對象只有2個狀態, 觸發(終止,true)、未觸發(非終止,false)。 未觸發不可調度,觸發可調度。
用戶模式構造:是由特殊CPU指令來協調線程,上節講的volatile實現就是一種,Interlocked也是。 也可稱為非阻塞線程同步。
二:WaitHandle
在windows編程中,我們通過API創建一個內核對象后會返回一個句柄,句柄則是每個進程句柄表的索引,而后可以拿到內核對象的指針、掩碼、標示等。
而WaitHandle抽象基類類作用是包裝了一個windows內核對象的句柄。我們來看下其中一個WaitOne的函數源碼(略精簡)。
[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);
}
//調用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 調用win32中,waitformultipleobjectsEx函數。
SignalAndWaitOne 調用win32中,signalandwait函數。
調用api帶ex都是設置超時的。 如果我們在c#中不傳,默認是-1 表示無限期等待。
其中SafeWaitHandle字段,包含了一個win32內核對象句柄。
理解了WaitHandle其他都好辦了,我們來看下它的派生類型。
三:AutoResetEvent
使用示例如下,有簡單注釋。 關于描述,盡量貼近系統自身術語。
}).Start();
new Thread(() =>
{
Thread.Sleep(2000);
Console.WriteLine("發信號");
waitHandler.Set(); //向內核對象發送信號。設置事件對象為非終止狀態、false,解除阻塞。
}).Start();
//waitHandler.Close(); //釋放句柄資源。
//waitHandler.Reset(); //手動設置事件為非終止狀態、false,線程阻止。
Console.ReadLine();
}
WaitOne 阻塞線程,非自旋。
Set() 發出一個信號后,設置事件狀態為false。 這本應該是2步的操作,AutoResetEvent.set()函數,給2步一起自動做了,很方便。
四:ManualResetEvent
這個和上面基本一樣,從字面來說需要手動重置狀態,我們來看例子。
manualWaitHandler.Reset(); //手動 設置事件對象狀態為非終止狀態,false。
manualWaitHandler.WaitOne(); //這里直接阻塞等待無效,因為事件對象還是true,必須手動調reset。
Console.WriteLine("第二次接收到信號,開始處理。");
}).Start();
new Thread(() =>
{
Thread.Sleep(2000);
Console.WriteLine("發信號");
manualWaitHandler.Set(); //向事件對象發送ok信號。。
Thread.Sleep(2000);
Console.WriteLine("第二次發信號");
manualWaitHandler.Set();
}).Start();
Console.ReadLine();
這2則區別很小,其實是系統Api的區分,不是net類庫實現的。
在Win32Native類中,我可以看到KERNEL32 api 有這么個參數isManualReset。
基于內核模式構造的同步步驟是: 托管代碼->用戶模式代碼->內核模式代碼。
用戶模式構造, 是利用CPU特殊指令,進行原子操作。
用戶模式代碼,如圖。 是指 托管代碼調用 win32代碼 這一層, 之后在調內核模式代碼。
新聞熱點
疑難解答