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