版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
第3章Windows程序的執(zhí)行單元——線程3.1多線程線程的創(chuàng)建線程內(nèi)核對象線程的終止線程的優(yōu)先級C/C++運行期庫線程的創(chuàng)建多線程并發(fā)執(zhí)行,os為其輪流分配cpu時間片一般情況,主線程接受用戶輸入,顯示運行結(jié)果;新創(chuàng)建的線程來處理長時間的操作,如讀寫文件、訪問網(wǎng)絡(luò)等,這樣即便程序忙于繁重的工作也可以由專門的線程響應(yīng)用戶命令每個線程必須擁有入口點函數(shù),主線程的為main。輔助線程入口點函數(shù)稱為線程函數(shù),定義如下:DWORDWINAPIThreadProc(LPVOIDlpParam);
函數(shù)名稱ThreadProc可以是任意的線程的創(chuàng)建#defineWINAPI_stdcall;_stdcall是新標(biāo)準(zhǔn)c/c++調(diào)用方法,與標(biāo)準(zhǔn)c調(diào)用_cdecl相比,參數(shù)進棧次序相同,從右到左,但_stdcall采用自動清棧方式,而_cdecl采用手工清棧方式。由windows操作系統(tǒng)調(diào)用的函數(shù)(回調(diào)函數(shù))必須聲明為_stdcall沒有顯示說明的話,為_cdecl調(diào)用方式線程的創(chuàng)建創(chuàng)建新線程用CreateThreadHANDLE
CreateThread(
LPSECURITY_ATTRIBUTES
lpsa,DWORD
cbStack,LPTHREAD_START_ROUTINE
lpStartAddr,LPVOID
lpvThreadParam,DWORD
fdwCreate, LPDWORD
lpIDThread
);
線程的創(chuàng)建lpsa
[in]Ignored.MustbeNULL.指定線程安全屬性cbStack
[in]指定線程堆棧大小,NULL為默認(rèn)大小lpStartAddr
[in]線程函數(shù)起始地址lpvThreadParam
[in]Longpointertoasingle32-bitparametervaluepassedtothethread.fdwCreate
[in]Specifiesflagsthatcontrolthecreationofthethread.0表示線程創(chuàng)建后立即運行l(wèi)pIDThread
[out]Longpointertoa32-bitvariablethatreceivesthethreadidentifier.IfthisparameterisNULL,thethreadidentifierisnotreturned.WaitForSingleObjectDWORD
WaitForSingleObject(HANDLE
hHandle,DWORD
dwMilliseconds
);
Thisfunctionreturnswhenthespecifiedobjectisinthesignaledstate(受信狀態(tài))orwhenthetime-outintervalelapses.該函數(shù)用于等待指定的對象(hHandle)變成受信狀態(tài)。 說明:在例03ThreadDemo工程中,用于等待輔助線程對應(yīng)的函數(shù)ThreadProc()執(zhí)行完成。調(diào)用該函數(shù)將阻塞主線程。一個可執(zhí)行對象有兩種狀態(tài):未受信、受信。線程對象只有當(dāng)線程結(jié)束才為受信態(tài)。WaitForSingleObjecthHandle
[in]Handletotheobject.Foralistoftheobjecttypeswhosehandlescanbespecified,seetheRemarkssection.dwMilliseconds
[in]Specifiesthetime-outinterval,inmilliseconds.Thefunctionreturnsiftheintervalelapses,eveniftheobject'sstateisnonsignaled.IfdwMillisecondsiszero,thefunctionteststheobject'sstateandreturnsimmediately.IfdwMillisecondsisINFINITE,thefunction'stime-outintervalneverelapses.線程的創(chuàng)建例子程序3.1.2線程內(nèi)核對象線程內(nèi)核對象是一個包含了線程狀態(tài)信息的數(shù)據(jù)結(jié)構(gòu)。每次對CreateThread的成功調(diào)用,系統(tǒng)都會在內(nèi)部為新的線程分配一個內(nèi)核對象。系統(tǒng)對線程的管理是依靠訪問線程內(nèi)核對象來實現(xiàn)的。1.線程上下文CONTEXT反映該線程上次運行時CPU寄存器的狀態(tài)。2.線程創(chuàng)建例子中使用計數(shù)調(diào)用CreateThread后,使用計數(shù)=2,線程函數(shù)返回,系統(tǒng)將使使用計數(shù)減少1,即使用計數(shù)=1,接下來又調(diào)用CloseHandle使使用計數(shù)減1,即最后使用計數(shù)=0,該內(nèi)存空間被收回。CONTEXT(上下文,即寄存器的狀態(tài)),恢復(fù)現(xiàn)場必須使用使用計數(shù)(2):CreateThread返回句柄,相當(dāng)于打開一次內(nèi)核對象,促使該值+1,所以初始值=2;另外,OpenThread使該值+1,GetCurrentThread不會改變該值暫停次數(shù)(1):1表示處于暫停狀態(tài),這樣就不會被調(diào)度到CPU中,給CreateThread時間對線程進行初始化。初始化之后,如果未傳遞CREATE_SUSPENDED(掛起)標(biāo)志,暫停次數(shù)=0,該線程處于可調(diào)度狀態(tài)。喚醒用ResumeThread(),該函數(shù)使暫停次數(shù)-1,掛起用SuspendThread(),該函數(shù)使暫停次數(shù)+1,一個線程可以被掛起若干次,這就需要喚醒同樣次數(shù)才能使暫停次數(shù)=0,才能處于可調(diào)度狀態(tài),大約每隔20ms,進行一次時間片輪轉(zhuǎn),os選擇新線程,將其上下文裝入cpu寄存器,組織起運行。退出代碼(STILL_ALIVE):即線程函數(shù)的返回值,可以用GetExitCodeThread得到線程退出代碼,所以可以當(dāng)作自定義的返回值來表示線程的執(zhí)行結(jié)果。是否受信(FALSE):只有當(dāng)線程結(jié)束時,該值才為TRUE…3.1.3線程的終止線程終止的4種方法線程函數(shù)自然退出ExitThreadTerminateThread造成無法作清除工作,ExitProcess不提倡例子程序02ExitThread3.1.4線程的優(yōu)先級每個線程被賦予一個優(yōu)先級號,0——31,0最低,31最高,調(diào)度時選擇最高的,并采用搶占式優(yōu)先調(diào)度算法。eg:IEexplor的線程優(yōu)先級就很高。設(shè)置優(yōu)先級用SetThreadPriority()。BOOL
SetThreadPriority(HANDLE
hThread,int
nPriority
);
THREAD_PRIORITY_TIME_CRITICAL實時THREAD_PRIORITY_HIGHEST最高THREAD_PRIORITY_ABOVE_NORMAL高于正常THREAD_PRIORITY_NORMAL正常THREAD_PRIORITY_BELOW_NORMAL低于正常THREAD_PRIORITY_LOWEST最低THREAD_PRIORITY_ABOVE_IDLE高于最低THREAD_PRIORITY_IDLE空閑線程的優(yōu)先級例子程序3.1.5C/C++運行期庫在實際的開發(fā)過程中,一般使用c/c++運行期函數(shù)_beginthreadx代替CreateThread函數(shù)。_beginthreadx首先申請一些用于線程同步的變量,然后調(diào)用CreateThreadVc++默認(rèn)的c/c++運行期庫不支持_beginthreadx,需要設(shè)置”Project/settings/c/c++/codegeneration/…./選中multithreadDLL”即可。需要#include<process.h>同樣,使用_endthreadex代替ExitThread。該函數(shù)首先釋放用于線程同步的變量,再調(diào)用ExitThread3.2線程同步同步可以保證在一個時間內(nèi)只有一個線程對某個共享資源有控制權(quán)。臨界區(qū)對象事件內(nèi)核對象線程局部存儲(TLS)3.2線程同步解決同步問題的方法:臨界區(qū)對象:線程獨占,等待的線程掛起,不可調(diào)度互鎖函數(shù):單一變量同步問題事件內(nèi)核對象:是否”受信”,通知信號量內(nèi)核對象:信號量計數(shù),多個線程共享,用于Socket(套接字)程序中線程同步互斥內(nèi)核對象:線程獨占,等待的線程可調(diào)度,用于保護內(nèi)存3.2.1臨界區(qū)對象1.為什么要線程同步?當(dāng)多個線程在同一個進程中執(zhí)行時,可能有不止一個線程同時執(zhí)行同一段代碼,訪問同一段內(nèi)存中的數(shù)據(jù)。線程同步產(chǎn)生的問題——一個錯誤的例子03ConntErr→線程函數(shù)ThreadFunc同時增加全局變量g_nCount1和g_nConnt2的計數(shù),原則上兩個變量的值應(yīng)該相等,但由于是兩個線程同時訪問這兩個全局變量,最終g_nCount1和g_nConnt2的值卻不相等。3.2.1臨界區(qū)對象1.為什么要線程同步?多線程同步要能保證在一個線程占有公共資源的時候,其他線程不會再占有這個資源。解決同步問題,就要保證整個存取過程的獨占性。2.使用臨界區(qū)對象臨界區(qū)對象是定義在數(shù)據(jù)段中的一個CRITICAL_SECTION結(jié)構(gòu),Windows內(nèi)部使用這個結(jié)構(gòu)紀(jì)錄一些同步信息,確保在同一時間只有一個線程訪問該數(shù)據(jù)段中的數(shù)據(jù)。使用臨界區(qū)對象void
InitializeCriticalSection(LPCRITICAL_SECTION
lpCriticalSection
);
ParameterslpCriticalSection
[in]Pointertothecriticalsectionobject.初始化臨界區(qū)使用臨界區(qū)對象void
EnterCriticalSection(LPCRITICAL_SECTION
lpCriticalSection
);
ParameterslpCriticalSection
[in]Pointertothecriticalsectionobject.申請進入臨界區(qū)使用臨界區(qū)對象void
LeaveCriticalSection(LPCRITICAL_SECTION
lpCriticalSection
);
ParameterslpCriticalSection
[in]Pointertothecriticalsectionobject.將臨界區(qū)交還給Windows,離開臨界區(qū)使用臨界區(qū)對象void
DeleteCriticalSection(LPCRITICAL_SECTION
lpCriticalSection
);
ParameterslpCriticalSection
[in]Pointertothecriticalsectionobject.整個程序不再使用臨界區(qū)的時候,將臨界區(qū)刪除使用臨界區(qū)對象使用方法說明及程序例子03CriticalSection臨界區(qū)對象能夠很好的保護共享數(shù)據(jù),但是它不能夠用于進程之間資源的鎖定,因為它不是內(nèi)核對象,如果要在進程間維持線程的同步,可以使用事件內(nèi)核對象。3.2.2互鎖函數(shù)互鎖函數(shù)為同步訪問多線程共享變量提供了一個簡單的機制。如果變量在共享內(nèi)存,不同進程的線程也可以使用此機制?;ユi函數(shù)包括:InterlockedIncrementInterlockedDecrementInterlockedExchangeAddInterlockedExchangePointer互鎖函數(shù)TheInterlockedIncrementfunctionincrements(increasesbyone)thevalueofthespecified32-bitvariableandcheckstheresultingvalue.Thefunctionpreventsmorethanonethreadfromusingthesamevariablesimultaneously.程序例子03InterlockDemowhile(g_bContinue) { ::InterlockedIncrement((long*)&g_nCount1); ::InterlockedIncrement((long*)&g_nCount2); }在主線程中等待子線程的結(jié)束,子線程的結(jié)束條件是g_bContinue=false,這就保證了在一個子線程中,g_nCount1和g_nCount2執(zhí)行了相同多次。產(chǎn)生不一致的唯一原因是加1的操作被打斷3.2.3事件內(nèi)核對象事件內(nèi)核對象主要用于線程間通信(同步就是一種簡單的通信,指通信的數(shù)據(jù)量少)因為它是一個內(nèi)核對象,所以可以跨進程使用,依靠通信,使各線程的工作協(xié)調(diào)進行,達到同步的目的。事件內(nèi)核對象包括3個成員nUsageCount(使用計數(shù))bManualReset(是否人工重置)bSignaled(是否受信)事件內(nèi)核對象使用WaitForSingleObject來判斷事件內(nèi)核對象是否受信,來達到通信的目的。使用CreateEvent函數(shù)創(chuàng)建事件對象。HANDLE
CreateEvent(
LPSECURITY_ATTRIBUTES
lpEventAttributes,安全屬性
BOOL
bManualReset,是否手動重置
BOOL
bInitialState,初始狀態(tài)
LPTSTR
lpName
);
事件對象名稱事件內(nèi)核對象設(shè)置事件對象名稱是為了在其他地方(如:其他進程的線程中)使用OpenEvent或者CreateEvent獲得此內(nèi)核對象句柄。HANDLEOpenEvent(DWORDdwDesiredAccess,訪問權(quán)限
BOOLbInheritHandle,句柄是否被繼承
LPCTSTRlpName);
事件對象名稱事件內(nèi)核對象BOOL
SetEvent(
HANDLE
hEvent
);
將事件對象狀態(tài)設(shè)置為“受信”BOOL
ResetEvent(HANDLEhEvent
);
將事件對象狀態(tài)設(shè)置為“未受信”程序例子事件內(nèi)核對象說明當(dāng)一個自動重置的事件對象受信以后,windows僅允許一個等待在該事件上的線程變?yōu)榭烧{(diào)度狀態(tài),然后就自動重置此事件對象為未受信狀態(tài)。(如果使用setevent,則上述動作是緊接著setevent進行,然后才執(zhí)行setevent后面的代碼)當(dāng)一個人工重置事件對象受信后,所有等待在該對象上的線程都變?yōu)榭烧{(diào)度狀態(tài)。線程同步問題實例問題描述:**********************************
*父親放蘋果,兒子吃蘋果,母親放香蕉,女兒吃香蕉
*在一個盤子之中只能放一樣水果,蘋果或香蕉
**********************************創(chuàng)建4個線程,分別代表父親、兒子、母親和女兒。主線程做初始化和收尾工作//放蘋果
UINTWINAPIPutAppleThread(PVOIDpvParam)
{
for(inti=0;i<10;i++)
{
WaitForSingleObject(g_dish,INFINITE);
WaitForSingleObject(g_putApple,INFINITE);
cout<<"putapple!\n";
SetEvent(g_eatApple);
Sleep(5);
}
return0;
}//放香蕉
UINTWINAPIPutBananaThread(PVOIDpvParam)
{
for(inti=0;i<10;i++)
{
WaitForSingleObject(g_dish,INFINITE);
WaitForSingleObject(g_putBanana,INFINITE);
cout<<"putbanana!\n";
SetEvent(g_eatBanana);
Sleep(5);
}
return0;
}//吃蘋果
UINTWINAPIEatAppleThread(PVOIDpvParam)
{
for(inti=0;i<10;i++)
{
WaitForSingleObject(g_eatApple,INFINITE);
cout<<"eatapple!\n";
SetEvent(g_putApple);
SetEvent(g_dish);;
}
return0;
}//吃香蕉
UINTWINAPIEatBananaThread(PVOIDpvParam)
{
for(inti=0;i<10;i++)
{
WaitForSingleObject(g_eatBanana,INFINITE);
cout<<"eatbanana!\n";
SetEvent(g_putBanana);
SetEvent(g_dish);
}
return0;
}intmain()
{
g_putApple=CreateEvent(NULL,FALSE,TRUE,NULL);
g_putBanana=CreateEvent(NULL,FALSE,TRUE,NULL);
g_eatApple=CreateEvent(NULL,FALSE,FALSE,NULL);
g_eatBanana=CreateEvent(NULL,FALSE,FALSE,NULL);
g_dish=CreateEvent(NULL,FALSE,TRUE,NULL);
HANDLEhThread[4];
intx;
hThread[0]=(HANDLE)_beginthreadex(NULL,0,PutAppleThread,(void*)&x,0,NULL);
hThread[1]=(HANDLE)_beginthreadex(NULL,0,PutBananaThread,(void*)&x,0,NULL);
hThread[2]=(HANDLE)_beginthreadex(NULL,0,EatAppleThread,(void*)&x,0,NULL);
hThread[3]=(HANDLE)_beginthreadex(NULL,0,EatBananaThread,(void*)&x,0,NULL);
charch;
cin>>ch;
return0;
}3.2.6線程局部存儲(TLS)線程局部存儲(thread-localstorage,TLS)是一個使用很方便的存儲線程局部數(shù)據(jù)的系統(tǒng)。利用TLS機制可以為進程中所有的線程關(guān)聯(lián)若干個數(shù)據(jù),各個線程通過由TLS分配的全局索引來訪問與自己關(guān)聯(lián)的數(shù)據(jù)。這樣,每個線程都可以有線程局部的靜態(tài)存儲數(shù)據(jù)。線程局部存儲(TLS)線程局部存儲(TLS)動態(tài)使用TLS的典型步驟如下。(1)主線程調(diào)用TlsAlloc函數(shù)為線程局部存儲分配索引,函數(shù)原型為:
DWORDTlsAlloc(void);
//返回一個TLS索引線程局部存儲(TLS)
系統(tǒng)為每一個進程都維護著一個長度為TLS_MINIMUM_AVAILABLE的位數(shù)組,TlsAlloc的返回值就是數(shù)組的一個下標(biāo)(索引)。這個位數(shù)組的惟一用途就是記憶哪一個下標(biāo)在使用中。初始狀態(tài)下,此位數(shù)組成員的值都是FREE,表示未被使用。當(dāng)調(diào)用TlsAlloc的時候,系統(tǒng)會挨個檢查這個數(shù)組中成員的值,直到找到一個值為FREE的成員。把找到的成員的值由FREE改為INUSE后,TlsAlloc函數(shù)返回該成員的索引。如果不能找到一個值為FREE的成員,TlsAlloc函數(shù)就返回TLS_OUT_OF_INDEXES(在WinBase.h文件中定義為-1),意味著失敗。線程局部存儲(TLS)(2)每個線程調(diào)用TlsSetValue和TlsGetValue設(shè)置或讀取線程數(shù)組中的值,函數(shù)原型為:BOOLTlsSetValue(DWORDdwTlsIndex,
//TLS索引
LPVOIDlpTlsValue
//要設(shè)置的值
);LPVOIDTlsGetValue(DWORDdwTlsIndex);
//TLS索引線程局部存儲(TLS)TlsSetValue和TlsGetValue分別用于設(shè)置和取得線程數(shù)組中的特定成員的值,而它們使用的索引就是TlsAlloc函數(shù)的返回值。這就充分說明了進程中惟一的位數(shù)組和各線程數(shù)組的關(guān)系。例如,TlsAlloc返回3,那就說明索引3被此進程中的每一個正在運行的和以后要被創(chuàng)建的線程保存起來,用以訪問各自線程數(shù)組中對應(yīng)的成員的值。線程局部存儲(TLS)(3)主線程調(diào)用TlsFree釋放局部存儲索引。函數(shù)的惟一參數(shù)是TlsAlloc返回的索引。利用TLS可以給特定的線程關(guān)聯(lián)一個數(shù)據(jù)。比如下面的例子將每個線程的創(chuàng)建時間與該線程關(guān)聯(lián)了起來,這樣,在線程終止的時候就可以得到線程的生命周期線程局部存儲(TLS)程序例子[實例]多線程文件搜索器structCDirectoryNode:
溫馨提示
- 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)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2026年武漢某國有企業(yè)招聘備考題庫及答案詳解一套
- 安徽醫(yī)科大學(xué)出版中心2026年度公開招聘編輯人員備考題庫及參考答案詳解1套
- 兒科醫(yī)生產(chǎn)科查房制度
- 倉庫車間生產(chǎn)管理制度
- 果凍生產(chǎn)安全管理制度
- 鈑金噴漆生產(chǎn)安全制度
- 蟲草生產(chǎn)管理制度
- 十一項安全生產(chǎn)制度
- 臘肉廠生產(chǎn)制度
- 生產(chǎn)招工制度
- 長期合作協(xié)議書合同書
- 浙江省小型液化天然氣氣化站技術(shù)規(guī)程
- 危化品安全管理培訓(xùn)課件
- 2023年高級售后工程師年度總結(jié)及下一年展望
- 小兒鞘膜積液
- 畢業(yè)設(shè)計粘土心墻土石壩設(shè)計含計算書cad圖
- 黑龍江省控制性詳細(xì)規(guī)劃編制規(guī)范
- 6工程竣工驗收交付證明書
- 《俠客風(fēng)云傳前傳》支線流程攻略1.0.2.4
- GB/T 12325-2008電能質(zhì)量供電電壓偏差
- 《抖音短視頻營銷存在的問題及對策10000字》
評論
0/150
提交評論