本章主要介紹下基于內(nèi)核模式構(gòu)造的線程同步方式,事件,信號(hào)量。
Windows的線程同步方式可分為2種,用戶模式構(gòu)造和內(nèi)核模式構(gòu)造。
內(nèi)核模式構(gòu)造:是由Windows系統(tǒng)本身使用,內(nèi)核對(duì)象進(jìn)行調(diào)度協(xié)助的。內(nèi)核對(duì)象是系統(tǒng)地址空間中的一個(gè)內(nèi)存塊,由系統(tǒng)創(chuàng)建維護(hù)。
內(nèi)核對(duì)象為內(nèi)核所擁有,而不為進(jìn)程所擁有,所以不同進(jìn)程可以訪問(wèn)同一個(gè)內(nèi)核對(duì)象,如進(jìn)程,線程,作業(yè),事件,文件,信號(hào)量,互斥量等都是內(nèi)核對(duì)象。
而信號(hào)量,互斥體,事件是Windows專門(mén)用來(lái)幫助我們進(jìn)行線程同步的內(nèi)核對(duì)象。
對(duì)于線程同步操作來(lái)說(shuō),內(nèi)核對(duì)象只有2個(gè)狀態(tài), 觸發(fā)(終止,true)、未觸發(fā)(非終止,false)。 未觸發(fā)不可調(diào)度,觸發(fā)可調(diào)度。
用戶模式構(gòu)造:是由特殊CPU指令來(lái)協(xié)調(diào)線程,上節(jié)講的volatile實(shí)現(xiàn)就是一種,Interlocked也是。 也可稱為非阻塞線程同步。
在windows編程中,通過(guò)API創(chuàng)建一個(gè)內(nèi)核對(duì)象后會(huì)返回一個(gè)句柄,句柄則是每個(gè)進(jìn)程句柄表的索引,而后可以拿到內(nèi)核對(duì)象的指針、掩碼、標(biāo)示等。
而WaitHandle抽象基類類作用是包裝了一個(gè)windows內(nèi)核對(duì)象的句柄。我們來(lái)看下其中一個(gè)WaitOne的函數(shù)源碼(略精簡(jiǎn))。
public virtual bool WaitOne(TimeSpan timeout) { return WaitOne(timeout, false); } [System.Security.SecuritySafeCritical] // auto-generated [SupWaitAll 和WaitAny 調(diào)用win32中,waitformultipleobjectsEx函數(shù)。
SignalAndWaitOne 調(diào)用win32中,signalandwait函數(shù)。
調(diào)用api帶ex都是設(shè)置超時(shí)的。 如果我們?cè)赾#中不傳,默認(rèn)是-1 表示無(wú)限期等待。
其中SafeWaitHandle字段,包含了一個(gè)win32內(nèi)核對(duì)象句柄。
理解了WaitHandle其他都好辦了,我們來(lái)看下它的派生類型。
WaitHandle |——EventWaitHandle 事件構(gòu)造。 |——AutoResetEvent |——ManualResetEvent |——Semaphore 信號(hào)量構(gòu)造。 |——Mutex 互斥體構(gòu)造。
其中Semaphore和mutex第一篇已經(jīng)介紹過(guò)了,下面來(lái)看看其他的。
AutoResetEvent
使用示例如下,有簡(jiǎn)單注釋。 關(guān)于描述,盡量貼近系統(tǒng)自身術(shù)語(yǔ)。
static void Main(string[] args) { //AutoResetEvent example //AutoResetEvent 通知正在等待的線程已發(fā)生的事件。 AutoResetEvent waitHandler = new AutoResetEvent(false);//false 即非終止,未觸發(fā)。 new Thread(() => { waitHandler.WaitOne(); //阻塞當(dāng)前線程,等待底層內(nèi)核對(duì)象收到信號(hào)。 Console.WriteLine("接收到信號(hào),開(kāi)始處理。"); }).Start(); new Thread(() => { Thread.Sleep(2000); Console.WriteLine("發(fā)信號(hào)"); waitHandler.Set(); //向內(nèi)核對(duì)象發(fā)送信號(hào)。設(shè)置事件對(duì)象為非終止?fàn)顟B(tài)、false,解除阻塞。 }).Start(); //waitHandler.Close(); //釋放句柄資源。 //waitHandler.Reset(); //手動(dòng)設(shè)置事件為非終止?fàn)顟B(tài)、false,線程阻止。 Console.ReadLine(); }WaitOne 阻塞線程,非自旋。
Set() 發(fā)出一個(gè)信號(hào)后,設(shè)置事件狀態(tài)為false。 這本應(yīng)該是2步的操作,AutoResetEvent.set()函數(shù),給2步一起自動(dòng)做了,很方便。
ManualResetEvent
這個(gè)和上面基本一樣,從字面來(lái)說(shuō)需要手動(dòng)重置狀態(tài),我們來(lái)看例子。
ManualResetEvent manualWaitHandler = new ManualResetEvent(false);//false 即非終止,未觸發(fā)。 new Thread(() => { manualWaitHandler.WaitOne(); //阻塞當(dāng)前線程對(duì)象,等待信號(hào)。 Console.WriteLine("接收到信號(hào),開(kāi)始處理。"); manualWaitHandler.Reset(); //手動(dòng) 設(shè)置事件對(duì)象狀態(tài)為非終止?fàn)顟B(tài),false。 manualWaitHandler.WaitOne(); //這里直接阻塞等待無(wú)效,因?yàn)槭录?duì)象還是true,必須手動(dòng)調(diào)reset。 Console.WriteLine("第二次接收到信號(hào),開(kāi)始處理。"); }).Start(); new Thread(() => { Thread.Sleep(2000); Console.WriteLine("發(fā)信號(hào)"); manualWaitHandler.Set(); //向事件對(duì)象發(fā)送ok信號(hào)。。 Thread.Sleep(2000); Console.WriteLine("第二次發(fā)信號(hào)"); manualWaitHandler.Set(); }).Start(); Console.ReadLine();這2者區(qū)別很小,其實(shí)是系統(tǒng)Api的區(qū)分,不是net類庫(kù)實(shí)現(xiàn)的。
在Win32Native類中,我可以看到KERNEL32 api 有這么個(gè)參數(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特殊指令,進(jìn)行原子操作。
用戶模式代碼,如圖。 是指 托管代碼調(diào)用 win32代碼 這一層, 之后在調(diào)內(nèi)核模式代碼。
參考CLR via c#及Windows核心編程第五版。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注