1、線(xiàn)程概述
WinCE是有優(yōu)先級(jí)的多任務(wù)操作系統(tǒng),它允許重功能、進(jìn)程在相同時(shí)間的系統(tǒng)中運(yùn)行,WinCE支持最大的32位同步進(jìn)程。一個(gè)進(jìn)程包括一個(gè)或多個(gè)線(xiàn)程,每個(gè)線(xiàn)程代表進(jìn)程的一個(gè)獨(dú)立部分,而一個(gè)線(xiàn)程被指定為進(jìn)程的基本線(xiàn)程。
WinCE以搶先方式來(lái)調(diào)度線(xiàn)程。線(xiàn)程以“時(shí)間片”為單位來(lái)運(yùn)行,WinCE的“時(shí)間片”通常為25毫秒。過(guò)來(lái)那個(gè)時(shí)間后,如果線(xiàn)程沒(méi)有放棄它的時(shí)間片,并且線(xiàn)程并不緊急,系統(tǒng)就會(huì)掛起線(xiàn)程并調(diào)度另一個(gè)線(xiàn)程來(lái)運(yùn)行。WinCE將根據(jù)優(yōu)先級(jí)方法來(lái)決定要運(yùn)行的線(xiàn)程,高優(yōu)先級(jí)的線(xiàn)程將在低優(yōu)先級(jí)的線(xiàn)程前面調(diào)度。
2、線(xiàn)程API函數(shù)
2.1 創(chuàng)建線(xiàn)程
WinCE提供了CreateThread函數(shù)來(lái)創(chuàng)建線(xiàn)程,其聲明如下: HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, //線(xiàn)程安全指針,不支持 DWORD dwStackSize, //為自己所使用堆棧分配的地址空間大小,不支持 LPTHREAD_START_ROUTINE lpStartAddress, //線(xiàn)程函數(shù)地址 LPVOID lpParameter, //傳入線(xiàn)程函數(shù)的參數(shù) DWORD dwCreationFlags, //控制線(xiàn)程創(chuàng)建的附加標(biāo)志 LPDWORD lpThreadId//新線(xiàn)程的ID值 );
WinCE不支持lpThreadAttributes和dwStackSize參數(shù),將它們?cè)O(shè)置成NULL和0即可。lpStartAddress指向線(xiàn)程函數(shù)的地址;lpParameter被傳遞到線(xiàn)程中的參數(shù);dwCreationFlags線(xiàn)程創(chuàng)建參數(shù),可以設(shè)置成0或CREATE_SUSPENDED,如果為0,表示線(xiàn)程立即執(zhí)行,如果參數(shù)為CREATE_SUSPENDED,則被創(chuàng)建的線(xiàn)程將處于掛起狀態(tài),而且必須要調(diào)用ResumeThread函數(shù)將其喚醒。
2.2掛起和恢復(fù)線(xiàn)程
正在運(yùn)行的線(xiàn)程可以被掛起、暫停執(zhí)行。同他使用SuspendThead函數(shù)即可實(shí)現(xiàn)以上功能,該函數(shù)的聲明如下:
DWORD SuspendThread( HANDLE hThread );
參數(shù)hThead代表要掛起線(xiàn)程的句柄。由于SuspendThread函數(shù)的調(diào)用將增加掛起計(jì)數(shù),因此在實(shí)際調(diào)度線(xiàn)程運(yùn)行之前,對(duì)SuspendThread函數(shù)的多次調(diào)用必須與對(duì)ResumeThread函數(shù)的多次調(diào)用相匹配。ResumeThread函數(shù)的定義
DWORD ResumeThread( HANDLE hThread );
參數(shù)hThead同樣代表要恢復(fù)線(xiàn)程的句柄。
3、線(xiàn)程同步
在使用線(xiàn)程時(shí),會(huì)經(jīng)常遇到兩個(gè)概念,即線(xiàn)程沖突和線(xiàn)程死鎖。
線(xiàn)程沖突:如果線(xiàn)程A讀寫(xiě)數(shù)據(jù)G,線(xiàn)程B也正在讀取數(shù)據(jù)G,那么很顯然,該操作將導(dǎo)致數(shù)據(jù)沖突,引起數(shù)據(jù)混亂。這里需要使用同步技術(shù),以保證線(xiàn)程A和線(xiàn)程B依次讀寫(xiě)數(shù)據(jù)G,避免數(shù)據(jù)沖突。
線(xiàn)程死鎖:例如A工人為加工III零件在等待B提供的I零件,而B(niǎo)正好在等待應(yīng)由A加工提供的II零件來(lái)裝配I零件。由于他們之間再?zèng)]有其他的任何人幫助通信或其他通信手段。所以他們一直在等對(duì)方的零件而進(jìn)入死鎖狀態(tài)。死鎖屬于邏輯錯(cuò)誤,無(wú)法通過(guò)線(xiàn)程同步來(lái)解決。
WinCE實(shí)現(xiàn)線(xiàn)程同步的常用方法:事件(Event)、互斥(Mutex)、信號(hào)量(Semaphore)、臨界區(qū)(CriticalSection)。
3.1 利用事件同步
“事件對(duì)象”是實(shí)現(xiàn)線(xiàn)程同步最基本的方法之一,一個(gè)事件對(duì)象可以處于“已標(biāo)示”和“未標(biāo)示”兩種狀態(tài),如果將事件對(duì)象設(shè)置為“已標(biāo)示”狀態(tài),表示可以執(zhí)行同步操作,事件對(duì)象處于“未標(biāo)示”狀態(tài),則表示需要等待事件對(duì)象變?yōu)?ldquo;已標(biāo)示”狀態(tài)才可以進(jìn)行同步操作。下面介紹利用事件同步所需要的API函數(shù)。
(1)CreateEvent函數(shù)。創(chuàng)建事件對(duì)象函數(shù)CreateEvent,其聲明如下:
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,//CE不支持,設(shè)為NULL
BOOL bManualReset, //設(shè)置是否手動(dòng)設(shè)置事件對(duì)象狀態(tài)
BOOL bInitialState, //事件對(duì)象初始狀態(tài)
LPTSTR lpName //事件對(duì)象名稱(chēng)
);
參數(shù)bManualReset表示是否手動(dòng)設(shè)置事件對(duì)象狀態(tài),當(dāng)其值為T(mén)RUE時(shí),在調(diào)用完等待函數(shù)(WaitForSingObject,WaitForMutipleObject)后,則必須調(diào)用ResetEvent函數(shù),以設(shè)置事件對(duì)象沒(méi)有被標(biāo)示,當(dāng)其值為FALSE時(shí),系統(tǒng)調(diào)用完等待函數(shù),會(huì)自動(dòng)將事件對(duì)象設(shè)置為未標(biāo)示狀態(tài)。
參數(shù)bInitialState表示事件對(duì)象初始狀態(tài),當(dāng)其值為T(mén)RUE是,事件對(duì)象初始化狀態(tài)為已標(biāo)示,當(dāng)其值為FALSE時(shí),事件對(duì)象初始狀態(tài)為未標(biāo)示。
如果創(chuàng)建事件函數(shù)對(duì)象CreateEvent執(zhí)行成功,將返回事件對(duì)象句柄。若失敗,則返回0,在不用事件句柄時(shí),需要使用CloseHandle()將其關(guān)閉,以釋放資源。
(2)SetEvent函數(shù)和ResetEvent函數(shù)。函數(shù)SetEvent()的功能是將事件對(duì)象設(shè)置為已標(biāo)示狀態(tài)。該函數(shù)的聲明如下:
BOOL SetEvent(HANDLE hEvent);
參數(shù)hEvent表示事件對(duì)象句柄。
函數(shù)ResetEvent函數(shù)功能將事件對(duì)象設(shè)置成未標(biāo)示狀態(tài),該函數(shù)的聲明如下:
BOOL ResetEvent(HANDLE hEvent);
(3) 使用事件同步的一般使用流程
通常情況,在主線(xiàn)程中,用戶(hù)利用CreateEvent函數(shù)創(chuàng)建一個(gè)事件對(duì)象,并且將參數(shù)bManualReset設(shè)為FALSE,參數(shù)bInitialState也設(shè)為FALSE,此時(shí)事件對(duì)象狀態(tài)未標(biāo)示。然后在線(xiàn)程里通過(guò)WaitForSingleObject函數(shù)來(lái)等待事件被標(biāo)示。此時(shí),只要在主線(xiàn)程中調(diào)用SetEvent函數(shù),將事件對(duì)象設(shè)置成已標(biāo)示。那么線(xiàn)程里的WaitForSingleObject函數(shù)便會(huì)返回,繼續(xù)執(zhí)行,同時(shí)將事件對(duì)象狀態(tài)設(shè)置成未標(biāo)示。
3.2 利用互斥同步
互斥同步類(lèi)似于事件對(duì)象同步;コ馔揭矊(chuàng)建一個(gè)互斥對(duì)象,該互斥對(duì)象也有“被線(xiàn)程擁有”和“不被線(xiàn)程擁有”兩種狀態(tài);當(dāng)互斥對(duì)象處于“不被線(xiàn)程擁有”狀態(tài),表示可以執(zhí)行相關(guān)操作;當(dāng)互斥對(duì)象處于“被線(xiàn)程擁有”狀態(tài),表示此時(shí)不可以執(zhí)行相關(guān)操作。通過(guò)等待函數(shù)請(qǐng)求互斥對(duì)象實(shí)現(xiàn)同步。
(1)CreateMutex函數(shù)。通過(guò)CreateMutex函數(shù)創(chuàng)建互斥對(duì)象,該函數(shù)定義如下:
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes, //CE不支持
BOOL bInitialOwner, //初始化擁有狀態(tài),TRUE表示擁有,F(xiàn)ALSE表示未被擁有
LPCTSTR lpName //互斥名稱(chēng)
);
如果創(chuàng)建互斥函數(shù)對(duì)象CreateMutex執(zhí)行成功,將返回互斥對(duì)象句柄。若失敗,則返回ERROR_INVALID_HANDLE,在不用互斥句柄時(shí),需要使用CloseHandle()將其關(guān)閉,以釋放資源。
(2)ReleaseMutex函數(shù)。在使用等待函數(shù)請(qǐng)求互斥對(duì)象時(shí),如果請(qǐng)求到互斥對(duì)象的擁有權(quán),則等待函數(shù)將自動(dòng)設(shè)置互斥對(duì)象狀態(tài)為“未被擁有”。ReleaseMutex函數(shù)負(fù)責(zé)釋放某個(gè)線(xiàn)程對(duì)象互斥對(duì)象的擁有權(quán),也就是將互斥對(duì)象設(shè)置為“未被線(xiàn)程擁有”狀態(tài)。ReleaseMutex函數(shù)定義如下:
BOOL ReleaseMutex( HANDLE hHandle);hHandle表示互斥對(duì)象句柄;
(3)利用互斥同步的一般使用流程
利用互斥同步的一般使用流程是:首先利用CreateMutex函數(shù)創(chuàng)建互斥對(duì)象,并將CreateMutex中的參數(shù)bInitialOwer設(shè)置為FALSE,使互斥對(duì)象處于“未被線(xiàn)程擁有”狀態(tài)。然后利用WaitForObject等待互斥對(duì)象,執(zhí)行相關(guān)操作。處理完成后,利用ReleaseMutex函數(shù)釋放線(xiàn)程對(duì)互斥對(duì)象的擁有權(quán)。當(dāng)所有線(xiàn)程執(zhí)行完畢后,需要使用CloseHandle()將其關(guān)閉。
3.3 利用臨界區(qū)同步
“臨界區(qū)”是進(jìn)行線(xiàn)程同步的另一種方法,它能夠阻止兩個(gè)或多個(gè)不同的線(xiàn)程在同一時(shí)間內(nèi)訪(fǎng)問(wèn)同一個(gè)代碼區(qū)域。它通過(guò)調(diào)用 EnterCriticalSection函數(shù)來(lái)指出已經(jīng)進(jìn)入代碼的臨界區(qū),如果另一線(xiàn)程也調(diào)用了EnterCritialSection函數(shù),并且參數(shù)指向同一臨界區(qū)對(duì)象,那么另一線(xiàn)程將阻塞,直到第一個(gè)線(xiàn)程調(diào)用了LeaveCriticalSection函數(shù)離開(kāi)臨界區(qū)為止。
臨界區(qū)同步所需要的API函數(shù):
(1) InitializeCriticalSection函數(shù)。如果要使用臨界區(qū),首先要使用InitializeCriticalSection函數(shù)創(chuàng)建臨界區(qū),該函數(shù)定義如下:
void InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
(2) DeleteCriticalSection 函數(shù),當(dāng)結(jié)束使用臨界區(qū)對(duì)象時(shí),必須調(diào)用DeleteCriticalSection 函數(shù)釋放臨界區(qū)對(duì)象所占有的資源。該函數(shù)定義如下:
void DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
(3)EnterCriticalSection函數(shù),在創(chuàng)建了臨界區(qū)對(duì)象后,需要調(diào)用EnterCriticalSection函數(shù)進(jìn)入臨界區(qū),以保護(hù)代碼,該函數(shù)定義如下:
void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
如果臨界區(qū)對(duì)象已經(jīng)屬于另一個(gè)線(xiàn)程,那么此函數(shù)將阻塞直到另一線(xiàn)程離開(kāi)臨界區(qū)才返回。
(4)LeaveCriticalSection函數(shù)。如果要離開(kāi)臨界區(qū),只需要調(diào)用LeaveCriticalSection函數(shù)即可。該函數(shù)定義如下:
void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
3.4 利用信號(hào)量同步
信號(hào)量是建立在互斥基礎(chǔ)上,并增加了資源計(jì)數(shù)的功能。它允許預(yù)定數(shù)目的線(xiàn)程同時(shí)進(jìn)入要同步的代碼。通過(guò)設(shè)置信號(hào)量計(jì)數(shù)為1,只允許一個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn)同步代碼,而實(shí)現(xiàn)線(xiàn)程同步。信號(hào)量同步所需要的API函數(shù):
(1) CreateSemaphore函數(shù)。在使用信號(hào)量實(shí)現(xiàn)同步時(shí),需要調(diào)用CreateSemaphore函數(shù)創(chuàng)建信號(hào)量對(duì)象。該函數(shù)定義如下:
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, //CE不支持
LONG lInitialCount, //信號(hào)量初始化計(jì)數(shù)值
LONG lMaximumCount, //信號(hào)量計(jì)數(shù)最大值
LPCTSTR lpName //信號(hào)量對(duì)象名稱(chēng)
);
如創(chuàng)建信號(hào)量成功,函數(shù)返回信號(hào)量對(duì)象句柄,否則返回NULL值。
(2)ReleaseSemaphore函數(shù)。在使用等待函數(shù)請(qǐng)求信號(hào)量時(shí),等待函數(shù)自動(dòng)給信號(hào)量計(jì)數(shù)減1,那么當(dāng)計(jì)數(shù)減到0時(shí),信號(hào)量對(duì)象將不能被請(qǐng)求。ReleaseSemaphore函數(shù)負(fù)責(zé)給信號(hào)量計(jì)數(shù)加值,使信號(hào)量可以被請(qǐng)求。此函數(shù)定義如下:
BOOL ReleaseSemaphore(
HANDLE hSemaphore, //信號(hào)量句柄
LONG lReleaseCount, //信號(hào)量計(jì)數(shù)增加的值
LPLONG lpPreviousCount //輸出量,表示上一次信號(hào)量計(jì)數(shù)
);
3.5 利用互鎖函數(shù)可對(duì)變量和指針進(jìn)行原子的讀/寫(xiě)操作。因?yàn)樗鼈儾恍枰~外的同步對(duì)象,所以有時(shí)這些互鎖函數(shù)特別有用。Windows ce提供的互鎖函數(shù)有:
InterlockedIncrement //把一個(gè)變量的值加1 InterlockedDecrement InterlockedExchange //交換兩個(gè)變量的值 InterlockedTestExchange //根據(jù)條件交換變量的值 InterlockedCompareExchange //根據(jù)比較原子交換 InterlockedCompareExchangePointer //根據(jù)比較原子交換指針 InterlockedExchangePointer //交換兩個(gè)指針的值 InterlockedExchangeAdd //給某個(gè)變量嗇某個(gè)特定值