版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
1、進(jìn)程/線程間同步 進(jìn)程/線程間同步 1. 線程控制2 進(jìn)程/線程間同步方法2.1 臨界區(qū)2.2 互斥量 2.3 信號量(燈)2.4 事件1. 線程控制 目前的操作系統(tǒng)大多提供了線程,一些編程工具如VC等語言也提供了線程實現(xiàn)的方法。線程與進(jìn)程的區(qū)別在于子線程與父線程運行在同一進(jìn)程空間內(nèi),而子進(jìn)程和父進(jìn)程則運行在不同的空間。這樣,同一進(jìn)程內(nèi)的不同線程間可以直接通過內(nèi)存交換數(shù)據(jù)(出于數(shù)據(jù)同步原因最好不要這樣做)。此外,在Win32的定義中一個進(jìn)程至少擁有一個線程,所以進(jìn)程也被叫做主線程。如何創(chuàng)建線程 MFC提供了對線程功能的封裝類CWinThread我們常使用的CWinApp類就是從這個類派生的。用
2、戶創(chuàng)建線程的函數(shù) AfxBeginThread()CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc, /函數(shù)入口地址 LPVOID pParam, / 是傳遞給線程的參數(shù) int nPriority = THREAD_PRIORITY_NORMAL, /表明線程的優(yōu)先級 UINT nStackSize = 0, /為棧大小,如果為0表示使用系統(tǒng)默認(rèn)值 DWORD dwCreateFlags = 0, /為創(chuàng)建線程時的標(biāo)記,為CREATE_SUSPENDED表示線程被創(chuàng)建后被掛起 LPSECURITY_ATTRIBUTES lpSec
3、urityAttrs = NULL /為安全屬性 ); 線程控制AfxBeginThread()函數(shù)的返回值是CWinThread類的指針,可以通過它實現(xiàn)對線程的控制。結(jié)束線程在線程函數(shù)返回時線程將被結(jié)束在線程內(nèi)部可以利用void AfxEndThread( UINT nExitCode );結(jié)束線程。 掛起和恢復(fù)DWORD CWinThread:SuspendThread( )DWORD CWinThread:ResumeThread( )獲取和設(shè)置線程優(yōu)先級int CWinThread:GetThreadPriority( )BOOL CWinThread:SetThreadPriorit
4、y( int nPriority )通過其他API函數(shù)對線程進(jìn)行操作CWinThread類中的成員變量:m_hThread和m_nThreadID保存了線程句柄和線程ID號??梢酝ㄟ^其他API函數(shù)對線程進(jìn)行操作。 在線程外部強(qiáng)制結(jié)束一個線程API函數(shù)BOOL TerminateThread( HANDLE hThread,DWORD dwExitCode );可以在線程外部強(qiáng)制結(jié)束一個線程但這樣做是有危險的,因為線程申請某些資源可能沒法釋放,而且也有可能引起進(jìn)程的崩潰。推薦的方法為設(shè)置一個標(biāo)記當(dāng)線程檢測到后自己退出,而不是采用從外部強(qiáng)制結(jié)束線程的方法。 2 進(jìn)程/線程間同步方法進(jìn)程/線程間同步
5、的方法比較多,每種方法都有不同的用途。臨界區(qū)、互斥量、信號量、事件。操作系統(tǒng)提供了多種同步對象供我們使用,并且可以替我們管理同步對象的加鎖和解鎖。我們需要做的就是對每個需要同步使用的資源產(chǎn)生一個同步對象,在使用該資源前申請加鎖,在使用完成后解鎖。 2.1 臨界區(qū)臨界區(qū)臨界區(qū)是一種最簡單的同步對象,它只可以在同一進(jìn)程內(nèi)部使用。它的作用是保證只有一個線程可以申請到該對象。 相關(guān)的API函數(shù): VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection );產(chǎn)生臨界區(qū) VOID DeleteCriticalSection(L
6、PCRITICAL_SECTION lpCriticalSection );刪除臨界區(qū) VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection );進(jìn)入臨界區(qū),相當(dāng)于申請加鎖,如果該臨界區(qū)正被其他線程使用則該函數(shù)會等待到其他線程釋放 BOOL TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection );進(jìn)入臨界區(qū),相當(dāng)于申請加鎖,和EnterCriticalSection不同如果該臨界區(qū)正被其他線程使用則該函數(shù)會立即返回FALSE,而不會等待 VOID Lea
7、veCriticalSection(LPCRITICAL_SECTION lpCriticalSection );退出臨界區(qū),相當(dāng)于申請解鎖 下面的示范代碼演示了如何使用臨界區(qū)來進(jìn)行數(shù)據(jù)同步處理: int iCounter=0; /全局變量CRITICAL_SECTION criCounter; / CRITICAL_SECTION是MFC定義的類型DWORD threadA(void* pD)int iID=(int)pD;for(int i=0;im_hThread;hThread1=pT2-m_hThread;hThread2=pT3-m_hThread;/等待這三個線程結(jié)束WaitFo
8、rMultipleObjects(3,hThread,TRUE,INFINITE);DeleteCriticalSection(&criCounter); /刪除臨界區(qū) printf(n over n); 2.2 互斥量 互斥量與臨界區(qū)的作用非常相似,但互斥量是可以命名的,也就是說它可以跨越進(jìn)程使用,所以創(chuàng)建互斥量需要的資源更多。如果只為了在進(jìn)程內(nèi)部使用的話,用臨界區(qū)會帶來速度上的優(yōu)勢并能夠減少資源占用量。因為互斥量是跨進(jìn)程的,互斥量一旦被創(chuàng)建,就可以通過名字打開它。 用在互斥量上的API函數(shù):HANDLE CreateMutex( /創(chuàng)建互斥量 LPSECURITY_ATTRIBUTES l
9、pMutexAttributes, / 安全信息 BOOL bInitialOwner, / 最初狀態(tài),為真,則表示創(chuàng)建它的線程直接擁有了該互斥量,而不需要再申請 LPCTSTR lpName / 名字,可以為NULL,但這樣一來就不能被其他線程/進(jìn)程打開);HANDLE OpenMutex(/打開一個存在的互斥量 DWORD dwDesiredAccess, / 存取方式 BOOL bInheritHandle, / 是否可以被繼承 LPCTSTR lpName / 名字);BOOL ReleaseMutex(/釋放互斥量的使用權(quán),但要求調(diào)用該函數(shù)的線程擁有該互斥量的使用權(quán) HANDLE h
10、Mutex / 句柄);BOOL CloseHandle(/關(guān)閉互斥量 HANDLE hObject / 句柄); DWORD WaitForSingleObject(/獲取互斥量的使用權(quán)需要使用函數(shù) HANDLE hHandle, / 等待的對象的句柄 DWORD dwMilliseconds / 等待的時間,以ms為單位,如果為INFINITE表示無限期的等待);其返回值的意義:WAIT_ABANDONED 在等待的對象為互斥量時表明因為互斥量被關(guān)閉而變?yōu)橛行盘枲顟B(tài)WAIT_OBJECT_0 得到使用權(quán)WAIT_TIMEOUT 超過(dwMilliseconds)規(guī)定時間 在線程調(diào)用Wai
11、tForSingleObject后,如果一直無法得到控制權(quán),線程將被掛起,直到超過時間或是獲得控制權(quán)。 WaitForSingleObject函數(shù)中的對象(Object)的含義這里的對象是一個具有信號狀態(tài)的對象,對象有兩種狀態(tài):有信號/無信號。而等待的含義就在于等待對象變?yōu)橛行盘柕臓顟B(tài),對于互斥量來講如果正在被使用則為無信號狀態(tài),被釋放后變?yōu)橛行盘枲顟B(tài)。當(dāng)?shù)却晒骔aitForSingleObject函數(shù)會將互斥量置為無信號狀態(tài),這樣其他的線程就不能獲得使用權(quán)而需要繼續(xù)等待。WaitForSingleObject函數(shù)還進(jìn)行排隊功能,保證先提出等待請求的線程先獲得對象的使用權(quán) 例:演示如何使用
12、互斥量來進(jìn)行同步(代碼的功能還是進(jìn)行全局變量遞增,通過輸出結(jié)果可以看出,先提出請求的線程先獲得了控制權(quán)):int iCounter=0;DWORD threadA(void* pD)int iID=(int)pD;/在內(nèi)部重新打開HANDLE hCounterIn=OpenMutex(MUTEX_ALL_ACCESS,FALSE,sam sp 44);for(int i=0;im_hThread;hThread1=pT2-m_hThread;hThread2=pT3-m_hThread;/等待三個線程都變?yōu)橛行盘枲顟B(tài),也就是說三個線程都結(jié)束WaitForMultipleObjects(3,hT
13、hread,TRUE,INFINITE);/ 等待線程CloseHandle(hCounter); /關(guān)閉句柄在這里沒有使用全局變量來保存互斥量句柄,這并不是因為不能這樣做,而是為演示如何在其他的代碼段中通過名字來打開已經(jīng)創(chuàng)建的互斥量。其實這個例子在邏輯上是有一點錯誤的,因為iCounter這個變量沒有跨進(jìn)程使用,所以沒有必要使用互斥量,只需要使用臨界區(qū)就可以了。從前面的例子中我們看到WaitForSingleObject這個函數(shù)將等待一個對象變?yōu)橛行盘枲顟B(tài),那么具有信號狀態(tài)的對象有哪些呢?下面是一部分: Mutex Event Semaphore Job Process Thread Wai
14、table timer Console input 互斥量(Mutex),信號燈(Semaphore),事件(Event)都可以被跨越進(jìn)程使用來進(jìn)行同步數(shù)據(jù)操作,而其他的對象與數(shù)據(jù)同步操作無關(guān),但對于進(jìn)程和線程來講,如果進(jìn)程和線程在運行狀態(tài)則為無信號狀態(tài),在退出后為有信號狀態(tài)。所以我們可以使用WaitForSingleObject來等待進(jìn)程和線程退出。我們在前面的例子中使用了WaitForMultipleObjects函數(shù),這個函數(shù)的作用與WaitForSingleObject類似,但從名字上我們可以看出,WaitForMultipleObjects將用于等待多個對象變?yōu)橛行盘枲顟B(tài),函數(shù)原型如
15、下: DWORD WaitForMultipleObjects( DWORD nCount, / 等待的對象數(shù)量CONST HANDLE *lpHandles, / 對象句柄數(shù)組指針 BOOL fWaitAll,/等待方式,為TRUE表示等待全部對象都變?yōu)橛行盘枲顟B(tài)才返回,為FALSE表示任何一個對象變?yōu)橛行盘枲顟B(tài)則返回 DWORD dwMilliseconds / 超時設(shè)置,以ms為單位,如果為INFINITE表示無限期的等待); 2.3 信號量(燈)信號燈對象可以說是一種資源計數(shù)器,一般有一個初始值,表示有多少進(jìn)程/線程可以進(jìn)入,當(dāng)信號燈的值大于0時為有信號狀態(tài),小于等于0時為無信號狀態(tài),
16、所以可以利用WaitForSingleObject進(jìn)行等待,當(dāng)WaitForSingleObject等待成功后信號燈的值會被減少1,直到釋放時信號燈會被增加1。 用于信號燈操作的API函數(shù) HANDLE CreateSemaphore(/創(chuàng)建信號燈 LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,/ 安全屬性,NULL表示使用默認(rèn)的安全描述 LONG lInitialCount, / 初始值 LONG lMaximumCount, / 最大值 LPCTSTR lpName / 名字);HANDLE OpenSemaphore(/打開信號燈 DWORD d
17、wDesiredAccess, / 存取方式 BOOL bInheritHandle, / 是否能被繼承 LPCTSTR lpName / 名字);BOOL ReleaseSemaphore(/釋放信號燈 HANDLE hSemaphore, / 句柄 LONG lReleaseCount, / 釋放數(shù),讓信號燈值增加數(shù) LPLONG lpPreviousCount / 用來得到釋放前信號燈的值,可以為NULL);BOOL CloseHandle(/關(guān)閉信號燈 HANDLE hObject / 句柄);信號燈的使用方式和互斥量的使用方式非常相似,下面的代碼使用初始值為2的信號燈來保證只有兩個線
18、程可以同時進(jìn)行數(shù)據(jù)庫調(diào)用:DWORD threadA(void* pD)int iID=(int)pD;/在內(nèi)部重新打開HANDLE hCounterIn=OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,sam sp 44);for(int i=0;im_hThread;hThread1=pT2-m_hThread;hThread2=pT3-m_hThread;/等待線程結(jié)束WaitForMultipleObjects(3,hThread,TRUE,INFINITE);/關(guān)閉句柄CloseHandle(hCounter);信號燈有時用來作為計數(shù)器使用,一般來講
19、將其初始值設(shè)置為0,先調(diào)用ReleaseSemaphore來增加其計數(shù),然后使用WaitForSingleObject來減小其計數(shù),遺憾的是通常我們都不能得到信號燈的當(dāng)前值,但是可以通過設(shè)置WaitForSingleObject的等待時間為0來檢查信號燈當(dāng)前是否為0。 2.4 事件 前面講的信號燈和互斥量可以保證資源被正常的分配和使用,而事件是用來通知其他進(jìn)程/線程某件操作已經(jīng)完成。 事件對象可以以兩種方式創(chuàng)建,一種為自動重置,在其他線程使用WaitForSingleObject等待到事件對象變?yōu)橛行盘柡笤撌录ο笞詣佑肿優(yōu)闊o信號狀態(tài),一種為人工重置在其他線程使用WaitForSingleOb
20、ject等待到事件對象變?yōu)橛行盘柡笤撌录ο鬆顟B(tài)不變。例如有多個線程都在等待一個線程運行結(jié)束,我們就可以使用人工重置事件,在被等待的線程結(jié)束時設(shè)置該事件為有信號狀態(tài),這樣其他的多個線程對該事件的等待都會成功(因為該事件的狀態(tài)不會被自動重置)。 事件相關(guān)的API如下: HANDLE CreateEvent(/創(chuàng)建事件對象 LPSECURITY_ATTRIBUTES lpEventAttributes,/ 安全屬性,NULL表示使用默認(rèn)的安全描述 BOOL bManualReset, / 是否為人工重置 BOOL bInitialState, / 初始狀態(tài)是否為有信號狀態(tài) LPCTSTR lpNa
21、me / 名字);HANDLE OpenEvent(/打開事件對象 DWORD dwDesiredAccess, / 存取方式 BOOL bInheritHandle, / 是否能夠被繼承 LPCTSTR lpName / 名字);BOOL ResetEvent(/設(shè)置事件為無信號狀態(tài) HANDLE hEvent / 句柄);BOOL SetEvent(/設(shè)置事件有無信號狀態(tài) HANDLE hEvent / 句柄);BOOL CloseHandle(/關(guān)閉事件對象 HANDLE hObject / 句柄);下面的代碼演示了自動重置和人工重置事件在使用中的不同效果: DWORD threadA(
22、void* pD)int iID=(int)pD;/在內(nèi)部重新打開HANDLE hCounterIn=OpenEvent(EVENT_ALL_ACCESS,FALSE,sam sp 44);printf(tthread %d beginn,iID);/設(shè)置成為有信號狀態(tài)Sleep(1000);SetEvent(hCounterIn);Sleep(1000);printf(tthread %d endn,iID);CloseHandle(hCounterIn);return 0;DWORD threadB(void* pD)/等待threadA結(jié)束后在繼續(xù)執(zhí)行int iID=(int)pD;/在
23、內(nèi)部重新打開HANDLE hCounterIn=OpenEvent(EVENT_ALL_ACCESS,FALSE,sam sp 44);if(WAIT_TIMEOUT = WaitForSingleObject(hCounterIn,10*1000)printf(ttthread %d wait time outn,iID);elseprintf(ttthread %d wait okn,iID);CloseHandle(hCounterIn);return 0;/in main functionHANDLE hCounter=NULL;if( (hCounter=OpenEvent(EVEN
24、T_ALL_ACCESS,FALSE,sam sp 44)=NULL)/如果沒有其他進(jìn)程創(chuàng)建這個事件,則重新創(chuàng)建,該事件為人工重置事件hCounter = CreateEvent(NULL,TRUE,FALSE,sam sp 44);/創(chuàng)建線程HANDLE hThread3;printf(test of manual rest eventn);CWinThread* pT1=AfxBeginThread(AFX_THREADPROC)threadA,(void*)1);CWinThread* pT2=AfxBeginThread(AFX_THREADPROC)threadB,(void*)2);CWinThread* pT3=AfxBeginThread(AFX_THREADPROC)threadB,(void*)3);hThread0=pT1-m_hThread;hThread1=pT2-m_hThread;hThread2=pT3-m_hThread;/等待線程結(jié)束W
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 企業(yè)六項制度
- 耐藥菌感染與醫(yī)療設(shè)備消毒策略
- 人民法庭安檢制度
- 醫(yī)院水泵房衛(wèi)生制度
- 紙質(zhì)餐飲具衛(wèi)生管理制度
- 衛(wèi)生室廢水消毒處理制度
- 耐藥機(jī)制與應(yīng)對策略
- 中轉(zhuǎn)站污水處理制度
- 2026年車載照明標(biāo)準(zhǔn)制定合作協(xié)議
- 老齡化基層醫(yī)療的醫(yī)養(yǎng)結(jié)合社會力量
- 重點傳染病診斷標(biāo)準(zhǔn)培訓(xùn)診斷標(biāo)準(zhǔn)
- 機(jī)柜端口對應(yīng)表
- GB/T 3934-2003普通螺紋量規(guī)技術(shù)條件
- 蘭渝鐵路指導(dǎo)性施工組織設(shè)計
- CJJ82-2019-園林綠化工程施工及驗收規(guī)范
- 小學(xué)三年級閱讀練習(xí)題《鴨兒餃子鋪》原文及答案
- 六宮格數(shù)獨100題
- 杭州電子招投標(biāo)系統(tǒng)使用辦法
- 車輛贈與協(xié)議模板
- CG5重力儀操作手冊
- 電解鋁項目投資計劃書(范文)
評論
0/150
提交評論