Windows® CE 系统中的同步机制
看到篇好文章,呵呵,獨樂樂,不如眾樂樂
?
本文轉(zhuǎn)自http://blog.csdn.net/thl789/archive/2006/01/17/582246.aspx ,轉(zhuǎn)載請注明出處
?
?
摘要
目錄
一、 WinCE進(jìn)程 /線程模型概覽
二、臨界區(qū)( Critical Section)
三、互斥體( Mutex)
四、信號量( Semaphore)
五、事件( Event)
六、消息隊列( MsgQueue P2P)
七、互鎖函數(shù)( Interlocked Function)
八、 Wait函數(shù)
總結(jié)
參考資料以及進(jìn)一步閱讀
關(guān)于作者
?
一、 WinCE 進(jìn)程 / 線程模型概覽
WinCE 操作系統(tǒng)實現(xiàn)了進(jìn)程 / 線程兩級管理模型。連同內(nèi)核進(jìn)程和系統(tǒng)進(jìn)程,以及應(yīng)用進(jìn)程一起, WinCE 共支持 32 個進(jìn)程。進(jìn)程還可以有自己的線程,進(jìn)程默認(rèn)有一個主線程,線程分為 256 個優(yōu)先級別, WinCE 調(diào)度程序按照線程優(yōu)先級高低來調(diào)度。進(jìn)程是 WinCE 中最小資源分配的單元,線程是 WinCE 的最小調(diào)度單元。
本文講述的同步機(jī)制有些只適用于線程間同步,有些既能用于線程間同步又能用于進(jìn)程間同步,下面討論到某一種機(jī)制的時候,再具體詳述其適用場景。
二、臨界區(qū)( Critical Section )
(本節(jié)內(nèi)容適用于 WinCE 1.0 及以上版本 )
WinCE 實現(xiàn)了操作系統(tǒng)理論里的臨界區(qū)管理。臨界區(qū)內(nèi)含有對臨街資源的訪問。通過對臨界區(qū)進(jìn)行有效管理,使得某一時刻最多只能有一個線程進(jìn)入臨界區(qū),實現(xiàn)對臨界資源的保護(hù)。
考慮下面用臨界區(qū)實現(xiàn)兩個線程對臨界資源互斥訪問的情形。 The 1st Thread 和 The 2nd Thread 都要調(diào)用 Func_CriticalSection() 函數(shù),而 Func_CriticalSection() 內(nèi)部會對某一臨界資源進(jìn)行操作,為了保護(hù)這一臨界資源,我們用一個 WinCE 的 CriticalSection 來實現(xiàn)。
圖一是該解決方案的一個場景。 The 1st Thread 和 The 2nd Thread 進(jìn)入臨界區(qū)之前已經(jīng)創(chuàng)建( new )并初始化( InitializeCriticalSection() )了一個臨界區(qū)。試圖進(jìn)入該臨界區(qū)的線程首先必須獲得進(jìn)入該臨界區(qū)(通過 EnterCriticalSection() / TryEnterCriticalSection() )的資格 ,如果臨界區(qū)內(nèi)沒有線程,它就能進(jìn)入,否則必須被掛起等待。進(jìn)入臨界區(qū)的線程可以對臨界資源進(jìn)行操作( OpOnSharedResources() )。操作完成之后退出臨界區(qū)( LeaveCriticalSection () ),以允許其它線程進(jìn)入。圖一中第一個線程進(jìn)入臨界區(qū)還未退出之前,第二個線程因執(zhí)行 EnterCriticalSection() 而 一直在被掛起等待,第一個線程退出臨界區(qū)之后,第二個線程從等待中被喚醒,按照相應(yīng)的調(diào)度機(jī)制重新競爭獲得 CPU ,從而繼續(xù)執(zhí)行,完成臨界區(qū)內(nèi)的操作。
?
圖一、應(yīng)用臨界區(qū)( CriticalSection )實現(xiàn)同步
利用臨界區(qū)可以實現(xiàn)對臨界資源的互斥操作, WinCE 的臨界區(qū)應(yīng)用在同一進(jìn)程內(nèi),亦即實現(xiàn)的是同一進(jìn)程內(nèi)的線程間同步,不能應(yīng)用在進(jìn)程之間。
三、互斥體( Mutex )
(本節(jié)內(nèi)容適用于 WinCE 1.01 及以上版本 )
互斥體( Mutex )顧名思義就是實現(xiàn)對共享資源實現(xiàn)互斥訪問的。 WinCE 中的互斥體的使用規(guī)則如下(按線程之間的同步為例):
◇ ? 互斥體可以是匿名互斥體也可以是命名互斥體;
◇ ? 線程創(chuàng)建互斥體的時候可以指定創(chuàng)建完畢它是否就立即擁有該互斥體;
◇ ? 某一時刻最多只有一個線程擁有給定的互斥體;
◇ ? 擁有互斥體的線程可多次獲得該互斥體;
◇ ? 線程可用 CreateMutex 或 wait函數(shù) 來獲得互斥體。
看下面應(yīng)用互斥體的情景。 Thread1 創(chuàng)建并擁有了一個互斥體 g_hMutex[ 序列 1&2] 。互斥體 g_hMutex 是定義的全局量, thread2 可訪問到, Thread2 用 WaitForSingleObject() 試圖獲得該互斥體,因為此時 g_hMutex 是被 Thread1 擁有的,所以 Thread2 被掛起 [ 序列 3] 。 Thread1 執(zhí)行了一些操作之后,又用 wait 函數(shù)試圖再次獲得了該互斥體,因為此時 g_hMutex 的擁有者還是 Thread1 ,所以 Thread1 立即再次獲得了該互斥體 [ 序列 4-6] 。 Thread1 對互斥體 g_hMutex 保護(hù)的共享資源操作完畢,釋放該互斥體 [ 序列 7] ,但是因為 Thread1 兩次獲得了 g_hMutex ,所以 g_hMutex 的擁有權(quán)并沒有交出。等到 Thread1 再次釋放互斥體 g_hMutex[ 序列 8] 之后, Thread1 才失去了 g_hMutex 的擁有權(quán), Thread2 可競爭 g_hMutex 的擁有權(quán),如能成功擁有,就可從等待狀態(tài)被喚醒,完成對共享資源的訪問操作。
?
圖二、應(yīng)用互斥體( Mutex )實現(xiàn)同步
不 知道從上面的描述,讀者有又沒有看出互斥體與臨界區(qū)之間的區(qū)別。使用上,它們都實現(xiàn)的對共享資源的互斥訪問,但是臨界區(qū)是多個線程對同一段程序的執(zhí)行,這 段程序會訪問到臨界資源,所以它們是同一個進(jìn)程內(nèi)的多個線程;而互斥體的應(yīng)用情景是在線程之間獨立執(zhí)行,可以不是程序上的重疊,只是一個線程執(zhí)行到共享資 源的時候,有可能別的線程也要訪問該共享資源,所以要用互斥體來保護(hù)該共享資源。
由于互斥體上述的應(yīng)用范圍,它不但能應(yīng)用在同一進(jìn)程內(nèi)的線程之間,也能應(yīng)用在進(jìn)程之間。進(jìn)程之間可以通過命名互斥體來實現(xiàn)。一個進(jìn)程通過為 CreateMutex() 指定一個名字做參數(shù)來獲得已經(jīng)存在的互斥體的句柄,處理過程如下面程序所示。
? HANDLE? hMutex;
? hMutex = CreateMutex (
??????????????? NULL,?????????????????????? //
??????????????? FALSE,????????????????????? // Mutex object NOT initially owned
??????????????? TEXT("NameOfMutexObject")); // Muetx Name
? if (NULL == hMutex)
? {
??? // Something wrong, deal with it here.
? }
? else
? {
??? if ( ERROR_ALREADY_EXISTS == GetLastError () )
??? {
????? // CreateMutex() opened existing mutex."
????? // ...
??? }
??? else
??? {
????? // CreateMutex() created new mutex."
????? // ...
??? }
? }
進(jìn)程獲得已經(jīng)存在的互斥體的句柄之后,就可以如線程之間同步規(guī)則那樣來實現(xiàn)進(jìn)程之間的互斥體使用。
四、信號量( Semaphore )
(本節(jié)內(nèi)容適用于 WinCE 3.0 及以上版本 )
信號量實體有一個數(shù)值指示當(dāng)前該信號量使用情況,當(dāng)前值的大小處于零和最大值之間。用下列操作原語實現(xiàn)信號量的同步操作(用線程間同步來說明):
◇ P ( S, num ) :如果信號量當(dāng)前值減去 num 大于零,執(zhí)行該操作的線程獲得信號量,可繼續(xù)執(zhí)行,同時信號量的當(dāng)前值減小 num ;否則訪問線程被掛起等待
◇ V ( S, num ) :信號量的當(dāng)前值增加 num (增加之后仍不大于最大值),如果有等待該信號量的線程被掛起,喚醒等待線程并按照相應(yīng)的調(diào)度機(jī)制參與調(diào)度。
信號量一般用來控制某類共享資源,最大值標(biāo)識該類資源的數(shù)目,執(zhí)行 P 操作是申請一定數(shù)目這類資源, V 操作是釋放一定數(shù)目的這類資源。在 WinCE 的信號量實現(xiàn)中,并未實現(xiàn) OpenSemaphore , P 操作是用 wait函數(shù) 來實現(xiàn)的,而 V 操作由 ReleaseSemaphore 來實現(xiàn)。
看下面用信號量來控制數(shù)量為 2 的某類共享資源的使用情景。
?
圖三、用信號量( Semaphore )實現(xiàn)同步
Thread1 創(chuàng)建一個控制 2 個共享資源的信號量 [ 序列 1&2] ,并且自己用 WaitForSingleObject() 來申請一個資源,因為當(dāng)前可用的這類資源有 2 個,所以它就獲得了其中的一個 [ 序列 3&4] 。同樣地, Thread2 獲得了另外一個資源 [ 序列 5&6] 。但是當(dāng) Thread3 也申請這類資源的時候,因為此時已經(jīng)沒有這類資源,信號量的值為零,它就被掛起 [ 序列 7] 。擁有這類資源的線程釋放掉一個資源 [ 序列 8&9] ,并且滿足能滿足 Thread3 申請資源數(shù)目的要求, Thread3 競爭獲得了該資源 [ 序列 10] 。
信號量是實現(xiàn)同步的基本方法,在幾乎所有的多任務(wù)操作系統(tǒng)里面都做了信號量的實現(xiàn),其它一些同步機(jī)制其實可以通過信號量來實現(xiàn)。如果把信號量的最大值和初始值均設(shè)置為 1 ,那么它就可實現(xiàn)互斥體 ,即保證對共享資源互斥訪問的保護(hù)。如果把信號量的初始值設(shè)置為 0 ,等待別的線程 ReleaseSemaphore 來喚醒它,那么它就可實現(xiàn)事件( Event ) 機(jī)制。
信號量機(jī)制可以用在同一進(jìn)程內(nèi)的線程之間同步,也可以用在進(jìn)程之間的同步。進(jìn)程間同步的實現(xiàn)方法如同互斥體的此類實現(xiàn)。
五、事件( Event )
(本節(jié)內(nèi)容適用于 WinCE 1.0 及以上版本 )
WinCE 系統(tǒng)中廣泛用到事件( Event )機(jī)制來實現(xiàn)線程之間的協(xié)調(diào)工作,具體表現(xiàn)在:
◇ 通知一個線程什么時候去執(zhí)行它的特定的任務(wù)
◇ 標(biāo)識事件的發(fā)生
WinCE 中的線程操作原語有 CreateEvent() , SetEvent()/PulseEvent() , ResetEvent() 等。創(chuàng)建 Event 的時候在 CreateEvent() 的參數(shù)中指定 Event 的初始狀態(tài)(觸發(fā)的 / 未觸發(fā)的),還要指定事件是否手動復(fù)位(手動復(fù)位是只有用 ResetEvent() 才能把事件狀態(tài)顯式地設(shè)置為未觸發(fā)的,自動復(fù)位是等待該事件的線程等待事件到來之后,系統(tǒng)自動把該事件的狀態(tài)復(fù)位為未觸發(fā)的)。線程等待事件仍然用 wait函數(shù) 。
下面是使用 Event 同步的簡單情況:
?
圖四、用事件( Event )實現(xiàn)同步
線程 Thread1 執(zhí)行過程中,要等到某個條件滿足(事件觸發(fā)),所以它創(chuàng)建了一個事件 Event (參數(shù)設(shè)置為:手動復(fù)位,初始條件為未觸發(fā)的),用 WaitForSingleObject() 來等待這個事件。線程 Thread2 執(zhí)行了一些操作之后,滿足了 Thread1 的條件,用 SetEvent 來觸發(fā)該事件。
?????? 除了可以用 SetEvent() 來觸發(fā)事件之外,也可以用 PulseEvent() 來觸發(fā),區(qū)別是 PulseEvent() 觸發(fā)該事件之后把它又復(fù)位。
?????? 另外,也可以把命名事件用于進(jìn)程之間的同步。實現(xiàn)方法同互斥體中的描述。
六、消息隊列( MsgQueue P2P )
(本節(jié)內(nèi)容適用于 WinCE.net 4.0 及以上版本 )
消息隊列通信機(jī)制如同建立了一個管道,管道的雙方通過分別建立到管道的兩端,與管道的讀端口建立連接的進(jìn)程可以從該端口讀取消息( Message ),與管道的寫端口建立連接的進(jìn)程可以寫入消息( Message )到管道。管道內(nèi)消息組成了一個 FIFO ( F irst I n F irst O ut )的隊列,從讀端口讀取消息是讀取隊列的頭,寫入消息到寫端口是在隊列尾部追加一個消息。
WinCE 中關(guān)于 MsgQueue 的操作函數(shù)主要有:
◇ CreateMsgQueue() 創(chuàng)建一個消息隊列。在該函數(shù)的參數(shù)中指定消息隊列的名字,消息隊列的最大數(shù)目,每個消息的最大長度,對該消息隊列可進(jìn)行讀還是寫操作等。因為調(diào)用一次 CreateMsgQueue 函數(shù),只能指定讀或者寫這樣的二選一的消息隊列,所以一般需要用相同的消息隊列名字做參數(shù)兩次調(diào)用該函數(shù),分別創(chuàng)建讀消息隊列和寫消息隊列,它們的返回值分別被讀進(jìn)程和寫進(jìn)程用 OpenMsgQueue() 打開用于讀取消息和寫入消息。
◇ OpenMsgQueue() 打開消息隊列并建立與相應(yīng)端口的連接。進(jìn)程與讀端口建立連接之后,可用返回的句柄從消息隊列中讀取消息;進(jìn)程與寫端口建立連接之后,可用返回的句柄寫入消息到消息隊列中。
◇ CloseMsgQueue() 斷開與消息隊列相應(yīng)的端口之間的連接,并關(guān)閉由 CreateMsgQueue() 或 OpenMsgQueue() 創(chuàng)建或打開的消息隊列。
◇ ReadMsgQueue() 如同從普通文件中讀取數(shù)據(jù)一樣,用于從消息隊列中讀取消息。可以指定讀取消息時,如果消息隊列為空,讀進(jìn)程是被掛起還是直接返回。
◇ WriteMsgQueue() 如同寫數(shù)據(jù)到普通文件中一樣,用于寫消息到消息隊列中。可以指定寫入消息時,如果消息隊列已滿,寫進(jìn)程是被掛起還是直接返回。
下圖是 MsgQueue 應(yīng)用的典型場景。
?
圖五、用消息隊列( MsgQueue )實現(xiàn)同步
這種場景下的執(zhí)行過程為:
◇ 主進(jìn)程 MainProcess 創(chuàng)建了名為“ Reader/Writer MsgQueue ”的讀和寫的消息隊列,并分別返回 hMsgQ_r_m 和 hMsgQ_w_m[ 序列 1-4] 。
◇ 讀進(jìn)程 ReaderProcess 以主進(jìn)程的 ProcessId 和 hMsgQ_r_m 為參數(shù),通過 OpenMsgQueue() 與 MainProcess 消息隊列的讀端口建立連接 [ 序列 5&6] 。
◇ ReaderProcess 與消息隊列建立連接之后,用 WaitForSingleOnject(hMsg_r) 看消息隊列中是否有消息,因為此時消息隊列為空,所以 ReaderProcess 被掛起 [ 序列 7] 。
◇ 寫進(jìn)程 WriterProcess 以主進(jìn)程的 ProcessId 和 hMsgQ_w_m 為參數(shù),通過 OpenMsgQueue() 與 MainProcess 消息隊列的寫端口建立連接 [ 序列 8&9] 。
◇ WriterProcess 與消息隊列建立連接之后,用 WaitForSingleOnject(hMsg_w) 看消息隊列中消息是否滿,因為此時消息隊列為空,未滿,所以 WriterProcess 不會被掛起 [ 序列 10&11] 。
◇ WriterProcess 寫消息到消息隊列中 [ 序列 12&13] 。
◇ 因為消息隊列中已經(jīng)有了消息, ReaderProcess 從掛起狀態(tài)被喚醒 [ 序列 14] 。
◇ ReaderProcess 繼續(xù)執(zhí)行,從消息隊列中讀取 WriterProcess 剛才寫入的消息。
消息隊列除可用于同步之外,主要用于進(jìn)程之間的數(shù)據(jù)傳遞,另外消息隊列也可以用于同一進(jìn)程中的線程之間同步,但是既然線程之間能直接傳遞數(shù)據(jù),又何必那么麻煩呢。
七、互鎖函數(shù)( Interlocked Function )
(本節(jié)內(nèi)容適用于 WinCE 1.0 及以上版本)
除了上面各節(jié)的同步方法之外, WinCE 還提供了一些用于原子操作的互鎖函數(shù),這些函數(shù)在執(zhí)行過程中,不會因為線程的調(diào)度引起的當(dāng)前線程被搶占而打斷函數(shù)內(nèi)的操作。
這些函數(shù)主要有:
InterlockedIncrement
InterlockedDecrement
InterlockedExchange
InterlockedTestExchange
InterlockedCompareExchange
InterlockedCompareExchangePointer
InterlockedExchangePointer
InterlockedExchangeAdd
八、 Wait 函數(shù)
(本節(jié)內(nèi)容適用于 WinCE 1.0 及以上版本 )
Wait 函數(shù)不是特指的某一個函數(shù),而是指 wait 的系列函數(shù)。 wait 函數(shù)并不是 WinCE 同步機(jī)制中的一種,但是 WinCE 的很多同步機(jī)制要用到 wait 函數(shù),這些在前面講述各個同步方法的時候也已有論述。
一般地,執(zhí)行 wait 函數(shù)時,如果等待的同步對象條件不滿足,那么執(zhí)行 wait 函數(shù)的進(jìn)程 / 線程會被掛起,當(dāng)然也可以給它們設(shè)置等待的超時時間,超過給定時間,不管條件是否滿足,它們會自動從等待狀態(tài)蘇醒。等待既可以等待某一個條件,也可以等待多個條件中的一個, WinCE 不支持等待多個條件同時滿足,如果有這種需要,要自己實現(xiàn)。
Wait 函數(shù)原型如下:
DWORD WaitForSingleObject (HANDLE hHandle, DWORD dwMilliseconds );
DWORD WaitForMultipleObjects (
? DWORD nCount,????????????? // No. of object handles in the array.
? CONST HANDLE* lpHandles,?? // Pointer to an array of object handles.
? BOOL fWaitAll,???????????? // MUST be FALSE in WinCE
? DWORD dwMilliseconds?????? // Timeout (0, mills, or INFINITE)
);
DWORD MsgWaitForMultipleObjects (
? DWORD nCount,???????????? // No. of object handles in the array.
? LPHANDLE pHandles,??????? // Pointer to an array of object handles.
? BOOL fWaitAll,??????????? // MUST be FALSE in WinCE
? DWORD dwMilliseconds,???? // Timeout (0, mills, or INFINITE)
? DWORD dwWakeMask????????? // Input types for which an input event object handle
);
前面講述各種同步機(jī)制的時候都是以 WaitForSingleObject() 來說明的,這里就不再贅述它了。
WaitForMultipleObjects() 和 MsgWaitForMultipleObjects() 可以用來等多個同步對象,它們之間的區(qū)別就是 MsgWaitForMultipleObjects() 還等待 dwWakeMask 參數(shù)中指定的輸入事件,即這些事件發(fā)生時,等待的進(jìn)程 / 線程也能被喚醒。
用 WaitForMultipleObjects() 等待的多個同步對象的句柄放在參數(shù) lpHandles 數(shù)組中,同步對象的句柄的數(shù)目放在參數(shù) nCount 中。 dwMilliseconds 指定了等待的超時參數(shù):如果指定為 0 ,該函數(shù)等待每個同步對象之后,不管觸發(fā)與否都直接返回;如果指定為 INFINITE ,該函數(shù)等待每個同步對象,直到有一個同步對象被觸發(fā),否則執(zhí)行該函數(shù)的運(yùn)行實體將一直被掛起;如果指定為非 0 ,非 INFINITE 的一個數(shù)值,那么不管 等待的同步對象是否被觸發(fā),到了指定的時間,執(zhí)行該函數(shù)而被掛起的運(yùn)行實體也會被喚醒。因哪個同步對象被觸發(fā)而返回還是因超時而返回,可以從返回值中來判定,返回值為 WAIT_TIMEOUT ,是因為超時; 返回值為 WAIT_OBJECT_0 到 WAIT_OBJECT_0 + nCount -1 之間的數(shù)時,可以按順序找到具體那個同步對象被觸發(fā) 。
下面是 WaitForMultipleObjects 的典型應(yīng)用。
? HANDLE hSynchObjects[EVENT_COUNT];
? DWORD dwEvent;
? /* Put event handles in hEvents */
? // ...
?
? dwEvent = WaitForMultipleObjects (
????????????????????? EVENT_COUNT,??????? // Number of objects in an array
????????????????????? hSynchObjects,????? // Array of objects
????????????????????? FALSE,????????????? // MUST be FALSE
????????????????????? 500);?????????????? // timeout, 0.5s
? switch (dwEvent)
? {
??? case WAIT_TIMEOUT:
????? // Handle for timeout
????? break;
?????
??? case WAIT_OBJECT_0 + 0:
????? // Handle the 1st event
????? break;
?????
??? case WAIT_OBJECT_0 + 1:
????? // Handle the 2nd one
????? break;
??? ...
???
??? case WAIT_OBJECT_0 + EVENT_COUNT -1:
????? // Handle the final one
????? break;
??? default:
????? // Error: Not an anticipant one, handle it.
????? break;
? }
總結(jié)
本文探討了 WinCE 中的各種同步機(jī)制的用法,并給出了它們的典型應(yīng)用場景。關(guān)于它們進(jìn)一步的高級話題,將在后續(xù)文章中探討。
參考資料以及進(jìn)一步閱讀
1) MSDN
2) UML Reference Manual, 2nd Edition
3 ) Abraham Silberschatz, Peter Baer Galvin, Greg Gagne. Operating System Concepts, 6th Edition. John Wiley & Sons, Inc/ 高等教育出版社影印 , 2002.5
4 ) David R. Butenhof/ 于磊,曾剛 . Programming with POSIX Threads. Addison Wesley/ 中國電力出版社 , 2003
?
總結(jié)
以上是生活随笔為你收集整理的Windows® CE 系统中的同步机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 人民币贬值是好事还是坏事,适度贬值不是坏
- 下一篇: java信息管理系统总结_java实现科