多個(gè)線程操作相同的數(shù)據(jù)時(shí),一般是需要按順序訪問的,否則會(huì)引導(dǎo)數(shù)據(jù)錯(cuò)亂,無(wú)法控制數(shù)據(jù),變成隨機(jī)變量。為解決這個(gè)問題,就需要引入互斥變量,讓每個(gè)線程都按順序地訪問變量。這樣就需要使用EnterCriticalSection和LeaveCriticalSection函數(shù)
有人比如的很形象:
就像上廁所:
門鎖了,就等著,等到別人出來了,進(jìn)去鎖上,然后該干什么干什么,干完了,把門打開
門沒鎖,就進(jìn)去,鎖上,然后該干什么干什么,干完了,把門打開
--------------------------------------------------
多線程中用來確保同一時(shí)刻只有一個(gè)線程操作被保護(hù)的數(shù)據(jù)
InitializeCriticalSection(&cs);//初始化臨界區(qū)
EnterCriticalSection(&cs);//進(jìn)入臨界區(qū)
//操作數(shù)據(jù)
MyMoney*=10;//所有訪問MyMoney變量的程序都需要這樣寫Enter.. Leave...
LeaveCriticalSection(&cs);//離開臨界區(qū)
DeleteCriticalSection(&cs);//刪除臨界區(qū)
實(shí)際遇到的問題:如多線程加載紋理過程中遇到的問題
step1.創(chuàng)建全局互斥變量,并初始化
static CRITICAL_SECTION gs_TextureLoadingCS; //全局互斥變量
static std::vector<CHRTextureLoadingReq> gs_TextureLoadingReqs; //全局紋理容器命令
CHRTextureMgrInstance::CHRTextureMgrInstance()

{
InitializeCriticalSection( &gs_TextureLoadingCS );
}

CHRTextureMgrInstance::~CHRTextureMgrInstance()

{
DeleteCriticalSection( &gs_TextureLoadingCS );
}

step2.開啟加載紋理多線程
BOOL CHRTextureMgrInstance::StartTextureLoadingThread()


{
gs_bTextureMgrWillDestroy = FALSE;
gs_bTextureLoadingThreadTerminated = FALSE;
//InitializeCriticalSection( &gs_TextureLoadingCS );
_beginthread( TextureLoadingThread, NULL, GetHREngine()->GetRenderer()->GetRealDevice() ); //開啟加載紋理線程
return TRUE;
}

void TextureLoadingThread(void* p)


{
// LPDIRECT3DDEVICE8 pD3DDevice = (LPDIRECT3DDEVICE8)p;
IHRRenderer* RI = GetHREngine()->GetRenderer();
// 當(dāng)主線程要結(jié)束了
while( !gs_bTextureMgrWillDestroy )

{
CHRTextureLoadingReq req;
BOOL bHasReq = FALSE;
EnterCriticalSection( &gs_TextureLoadingCS ); //進(jìn)入臨界區(qū)
if( gs_TextureLoadingReqs.size() > 0 ) //操作,取出一個(gè)紋理加載

{
bHasReq = TRUE;
req = gs_TextureLoadingReqs[0];
gs_TextureLoadingReqs.erase( gs_TextureLoadingReqs.begin() );
}
LeaveCriticalSection( &gs_TextureLoadingCS ); //離開臨界區(qū)
if( bHasReq )

{
if( CreateTextureFromReq( RI, &req ) )

{
PostTextureLoadingAck( req );
}
}
Sleep( 1 );
}
// 這個(gè)線程結(jié)束了
gs_bTextureLoadingThreadTerminated = TRUE;
}

step2.讀取圖片并壓入容器,邊壓入,邊讀取

g_nGlobalTextures[eFootprint] = pMgr->RegisterTexture( "Data\\Textures\\Effect\\Footprint.tga", TRUE, 0, TRUE );
g_nGlobalTextures[eSmoke] = pMgr->RegisterTexture( "Data\\Textures\\Effect\\Smoke.tga", TRUE, 0, TRUE );
g_nGlobalTextures[eShadow] = pMgr->RegisterTexture( "Data\\Textures\\Effect\\Shadow.tga", TRUE, 0, TRUE );
g_nGlobalTextures[eHitFlash] = pMgr->RegisterTexture( "Data\\Textures\\Effect\\HitFlash.tga", TRUE, 0, TRUE );
g_nGlobalTextures[eElectric] = pMgr->RegisterTexture( "Data\\Textures\\Effect\\LightingRed.tga", TRUE, 0, TRUE );
BOOL PostTextureLoadingReq( CHRTextureLoadingReq& req )

{
EnterCriticalSection( &gs_TextureLoadingCS );
gs_TextureLoadingReqs.push_back( req );
LeaveCriticalSection( &gs_TextureLoadingCS );
return TRUE;
}


舉個(gè)簡(jiǎn)單的例子:
#include "stdafx.h"
#include <Windows.h>
#include <iostream>
#include <process.h>
using std::cout;
using std::endl;


CRITICAL_SECTION cs;
UINT n_AddValue = 0;

// first thread
void FirstThread( LPVOID lParam )


{

for( int i = 0; i < 100; i++ )
{
EnterCriticalSection( &cs );
n_AddValue++;
cout << "n_AddValue in FirstThread is "<<n_AddValue <<endl;
LeaveCriticalSection( &cs );
}
}

// second thread
void SecondThread( LPVOID lParam )


{

for( int i = 0; i < 100; i++ )
{
EnterCriticalSection( &cs );
n_AddValue++;
cout << "n_AddValue in SecondThread is "<<n_AddValue <<endl;
LeaveCriticalSection( &cs );
}

}


void main()


{
InitializeCriticalSection( &cs );
HANDLE hThread[2];
hThread[0] = (HANDLE)_beginthread( FirstThread, 0, LPVOID(NULL) );
hThread[1] = (HANDLE)_beginthread( SecondThread, 0, LPVOID(NULL) );
// 等待線程返回
WaitForMultipleObjects( 2, hThread, true, INFINITE );

DeleteCriticalSection( &cs );
system("pause");
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
臨界區(qū)的理解:
理解EnterCriticalSection 臨界區(qū)
比如說我們定義了一個(gè)共享資源dwTime[100],兩個(gè)線程ThreadFuncA和ThreadFuncB都對(duì)它進(jìn)行讀寫操作。當(dāng)我們想要保證 dwTime[100]的操作完整性,即不希望寫到一半的數(shù)據(jù)被另一個(gè)線程讀取,那么用CRITICAL_SECTION來進(jìn)行線程同步如下:
第一個(gè)線程函數(shù):
DWORD WINAPI ThreadFuncA(LPVOID lp) { EnterCriticalSection(&cs); ... // 操作dwTime ... LeaveCriticalSection(&cs); return 0; }
寫出這個(gè)函數(shù)之后,很多初學(xué)者都會(huì)錯(cuò)誤地以為,此時(shí)cs對(duì)dwTime進(jìn)行了鎖定操作,dwTime處于cs的保護(hù)之中。一個(gè)“自然而然”的想法就是——cs和dwTime一一對(duì)應(yīng)上了。
這么想,就大錯(cuò)特錯(cuò)了。dwTime并沒有和任何東西對(duì)應(yīng),它仍然是任何其它線程都可以訪問的。如果你像如下的方式來寫第二個(gè)線程,那么就會(huì)有問題:
DWORD WINAPI ThreadFuncB(LPVOID lp) { ... // 操作dwTime ... return 0; }
當(dāng)線程ThreadFuncA執(zhí)行了EnterCriticalSection(&cs),并開始操作dwTime[100]的時(shí)候,線程 ThreadFuncB可能隨時(shí)醒過來,也開始操作dwTime[100],這樣,dwTime[100]中的數(shù)據(jù)就被破壞了。
為了讓CRITICAL_SECTION發(fā)揮作用,我們必須在訪問dwTime的任何一個(gè)地方都加上 EnterCriticalSection(&cs)和LeaveCriticalSection(&cs)語(yǔ)句。所以,必須按照下面的方式來寫第二個(gè)線程函數(shù):
DWORD WINAPI ThreadFuncB(LPVOID lp) { EnterCriticalSection(&cs); ... // 操作dwTime ... LeaveCriticalSection(&cs); return 0; }
這樣,當(dāng)線程ThreadFuncB醒過來時(shí),它遇到的第一個(gè)語(yǔ)句是EnterCriticalSection(&cs),這個(gè)語(yǔ)句將對(duì)cs變量進(jìn)行訪問。如果這個(gè)時(shí)候第一個(gè)線程仍然在操作dwTime[100],cs變量中包含的值將告訴第二個(gè)線程,已有其它線程占用了cs。因此,第二個(gè)線程的 EnterCriticalSection(&cs)語(yǔ)句將不會(huì)返回,而處于掛起等待狀態(tài)。直到第一個(gè)線程執(zhí)行了 LeaveCriticalSection(&cs),第二個(gè)線程的EnterCriticalSection(&cs)語(yǔ)句才會(huì)返回,并且繼續(xù)執(zhí)行下面的操作。
這個(gè)過程實(shí)際上是通過限制有且只有一個(gè)函數(shù)進(jìn)入CriticalSection變量來實(shí)現(xiàn)代碼段同步的。簡(jiǎn)單地說,對(duì)于同一個(gè) CRITICAL_SECTION,當(dāng)一個(gè)線程執(zhí)行了EnterCriticalSection而沒有執(zhí)行LeaveCriticalSection的時(shí)候,其它任何一個(gè)線程都無(wú)法完全執(zhí)行EnterCriticalSection而不得不處于等待狀態(tài)。
再次強(qiáng)調(diào)一次,沒有任何資源被“鎖定”,CRITICAL_SECTION這個(gè)東東不是針對(duì)于資源的,而是針對(duì)于不同線程間的代碼段的!我們能夠用它來進(jìn)行所謂資源的“鎖定”,其實(shí)是因?yàn)槲覀冊(cè)谌魏卧L問共享資源的地方都加入了EnterCriticalSection和 LeaveCriticalSection語(yǔ)句,使得同一時(shí)間只能夠有一個(gè)線程的代碼段訪問到該共享資源而已(其它想訪問該資源的代碼段不得不等待)。
這就是使用一個(gè)CRITICAL_SECTION時(shí)的情況。你應(yīng)該要知道,它并沒有什么可以同步的資源的“集合”。這個(gè)概念不正確。
如果是兩個(gè)CRITICAL_SECTION,就以此類推。
|
雖然臨界區(qū)同步速度很快,但卻只能用來同步本進(jìn)程內(nèi)的線程,而不可用來同步多個(gè)進(jìn)程中的線程。
MFC提供了很多功能完備的類,我用MFC實(shí)現(xiàn)了臨界區(qū)。MFC為臨界區(qū)提供有一個(gè)CCriticalSection類,使用該類進(jìn)行線程同步處理是非常簡(jiǎn)單的。只需在線程函數(shù)中用CCriticalSection類成員函數(shù)Lock()和UnLock()標(biāo)定出被保護(hù)代碼片段即可。Lock()后代碼用到的資源自動(dòng)被視為臨界區(qū)內(nèi)的資源被保護(hù)。UnLock后別的線程才能訪問這些資源。
posted on 2010-07-08 10:39
風(fēng)輕云淡 閱讀(1842)
評(píng)論(0) 編輯 收藏 引用 所屬分類:
C++