多个线程操作相同的数据时,一般是需要按顺序访问的,否则会引导数据错乱,无法控制数据,变成随机变量。为解决这个问题,就需要引入互斥变量,让每个线程都按顺序地访问变量。这样就需要使用EnterCriticalSection和LeaveCriticalSection函数
有人比如的很形象:
就像上厕所:
门锁了,就等着,等到别人出来了,进去锁上,然后该干什么干什么,干完了,把门打开
门没锁,就进去,锁上,然后该干什么干什么,干完了,把门打开
--------------------------------------------------
多线程中用来确保同一时刻只有一个线程操作被保护的数据
InitializeCriticalSection(&cs);//初始化临界区 
EnterCriticalSection(&cs);//进入临界区
//操作数据
MyMoney*=10;//所有访问MyMoney变量的程序都需要这样写Enter.. Leave...
LeaveCriticalSection(&cs);//离开临界区
DeleteCriticalSection(&cs);//删除临界区 
实际遇到的问题:如多线程加载纹理过程中遇到的问题
 step1.创建全局互斥变量,并初始化
step1.创建全局互斥变量,并初始化 
 static CRITICAL_SECTION gs_TextureLoadingCS;            //全局互斥变量
    static CRITICAL_SECTION gs_TextureLoadingCS;            //全局互斥变量
 static std::vector<CHRTextureLoadingReq> gs_TextureLoadingReqs;        //全局纹理容器命令
    static std::vector<CHRTextureLoadingReq> gs_TextureLoadingReqs;        //全局纹理容器命令
 
    
 CHRTextureMgrInstance::CHRTextureMgrInstance()
    CHRTextureMgrInstance::CHRTextureMgrInstance()

 
     {
{
 InitializeCriticalSection( &gs_TextureLoadingCS );
        InitializeCriticalSection( &gs_TextureLoadingCS );
 }
    }

 CHRTextureMgrInstance::~CHRTextureMgrInstance()
    CHRTextureMgrInstance::~CHRTextureMgrInstance()

 
     {
{
 DeleteCriticalSection( &gs_TextureLoadingCS );
        DeleteCriticalSection( &gs_TextureLoadingCS );
 }
    }

 step2.开启加载纹理多线程
step2.开启加载纹理多线程
 BOOL CHRTextureMgrInstance::StartTextureLoadingThread()
BOOL CHRTextureMgrInstance::StartTextureLoadingThread()


 {
{
 gs_bTextureMgrWillDestroy = FALSE;
    gs_bTextureMgrWillDestroy = FALSE;
 gs_bTextureLoadingThreadTerminated = FALSE;
    gs_bTextureLoadingThreadTerminated = FALSE;
 //InitializeCriticalSection( &gs_TextureLoadingCS );
    //InitializeCriticalSection( &gs_TextureLoadingCS );
 _beginthread( TextureLoadingThread, NULL, GetHREngine()->GetRenderer()->GetRealDevice() );  //开启加载纹理线程
    _beginthread( TextureLoadingThread, NULL, GetHREngine()->GetRenderer()->GetRealDevice() );  //开启加载纹理线程
 return TRUE;
    return TRUE;
 }
}

 void TextureLoadingThread(void* p)
void TextureLoadingThread(void* p)


 {
{
 //    LPDIRECT3DDEVICE8 pD3DDevice = (LPDIRECT3DDEVICE8)p;
//    LPDIRECT3DDEVICE8 pD3DDevice = (LPDIRECT3DDEVICE8)p;
 IHRRenderer* RI = GetHREngine()->GetRenderer();
    IHRRenderer* RI = GetHREngine()->GetRenderer();
 // 当主线程要结束了
    // 当主线程要结束了
 while( !gs_bTextureMgrWillDestroy )
    while( !gs_bTextureMgrWillDestroy )

 
     {
{
 CHRTextureLoadingReq req;
        CHRTextureLoadingReq req;
 BOOL bHasReq = FALSE;
        BOOL bHasReq = FALSE;
 EnterCriticalSection( &gs_TextureLoadingCS );                            //进入临界区
        EnterCriticalSection( &gs_TextureLoadingCS );                            //进入临界区
 if( gs_TextureLoadingReqs.size() > 0 )                                    //操作,取出一个纹理加载
        if( gs_TextureLoadingReqs.size() > 0 )                                    //操作,取出一个纹理加载

 
         {
{
 bHasReq = TRUE;
            bHasReq = TRUE;
 req = gs_TextureLoadingReqs[0];
            req = gs_TextureLoadingReqs[0];
 gs_TextureLoadingReqs.erase( gs_TextureLoadingReqs.begin() );
            gs_TextureLoadingReqs.erase( gs_TextureLoadingReqs.begin() );
 }
        }
 LeaveCriticalSection( &gs_TextureLoadingCS );                            //离开临界区
        LeaveCriticalSection( &gs_TextureLoadingCS );                            //离开临界区 
 if( bHasReq )
        if( bHasReq )

 
         {
{
 if( CreateTextureFromReq( RI, &req ) )
            if( CreateTextureFromReq( RI, &req ) )

 
             {
{
 PostTextureLoadingAck( req );
                PostTextureLoadingAck( req );
 }
            }
 }
        }
 Sleep( 1 );
        Sleep( 1 );
 }
    }
 // 这个线程结束了
    // 这个线程结束了
 gs_bTextureLoadingThreadTerminated = TRUE;
    gs_bTextureLoadingThreadTerminated = TRUE;
 }
}

 step2.读取图片并压入容器,边压入,边读取
step2.读取图片并压入容器,边压入,边读取

 g_nGlobalTextures[eFootprint]    = pMgr->RegisterTexture( "Data\\Textures\\Effect\\Footprint.tga", TRUE, 0, TRUE );
    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[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[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[eHitFlash]    = pMgr->RegisterTexture( "Data\\Textures\\Effect\\HitFlash.tga", TRUE, 0, TRUE );
 g_nGlobalTextures[eElectric]    = pMgr->RegisterTexture( "Data\\Textures\\Effect\\LightingRed.tga", TRUE, 0, TRUE );
    g_nGlobalTextures[eElectric]    = pMgr->RegisterTexture( "Data\\Textures\\Effect\\LightingRed.tga", TRUE, 0, TRUE );
 
    
 BOOL PostTextureLoadingReq( CHRTextureLoadingReq& req )
    BOOL PostTextureLoadingReq( CHRTextureLoadingReq& req )

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


举个简单的例子:
 #include "stdafx.h"
#include "stdafx.h"
 #include <Windows.h>
#include <Windows.h>
 #include <iostream>
#include <iostream>
 #include <process.h>
#include <process.h>
 using std::cout;
using std::cout;
 using std::endl;
using std::endl;


 CRITICAL_SECTION cs;
CRITICAL_SECTION cs;
 UINT n_AddValue  = 0;
UINT n_AddValue  = 0;

 // first thread
// first thread
 void FirstThread( LPVOID lParam )
void FirstThread( LPVOID lParam )


 {
{

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

 // second thread
// second thread
 void  SecondThread( LPVOID lParam )
void  SecondThread( LPVOID lParam )


 {
{

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

 }
}


 void main()
void main()


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

 DeleteCriticalSection( &cs );
    DeleteCriticalSection( &cs );
 system("pause");
    system("pause");
 }
}----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
临界区的理解:
理解EnterCriticalSection 临界区
 
 
    
        
            | 比如说我们定义了一个共享资源dwTime[100],两个线程ThreadFuncA和ThreadFuncB都对它进行读写操作。当我们想要保证 dwTime[100]的操作完整性,即不希望写到一半的数据被另一个线程读取,那么用CRITICAL_SECTION来进行线程同步如下:
 第一个线程函数:
 
 DWORD   WINAPI   ThreadFuncA(LPVOID   lp)
 {
 EnterCriticalSection(&cs);
 ...
 //   操作dwTime
 ...
 LeaveCriticalSection(&cs);
 return   0;
 }
 
 写出这个函数之后,很多初学者都会错误地以为,此时cs对dwTime进行了锁定操作,dwTime处于cs的保护之中。一个“自然而然”的想法就是——cs和dwTime一一对应上了。
 
 这么想,就大错特错了。dwTime并没有和任何东西对应,它仍然是任何其它线程都可以访问的。如果你像如下的方式来写第二个线程,那么就会有问题:
 
 DWORD   WINAPI   ThreadFuncB(LPVOID   lp)
 {
 ...
 //   操作dwTime
 ...
 return   0;
 }
 
 当线程ThreadFuncA执行了EnterCriticalSection(&cs),并开始操作dwTime[100]的时候,线程 ThreadFuncB可能随时醒过来,也开始操作dwTime[100],这样,dwTime[100]中的数据就被破坏了。
 
 为了让CRITICAL_SECTION发挥作用,我们必须在访问dwTime的任何一个地方都加上 EnterCriticalSection(&cs)和LeaveCriticalSection(&cs)语句。所以,必须按照下面的方式来写第二个线程函数:
 
 DWORD   WINAPI   ThreadFuncB(LPVOID   lp)
 {
 EnterCriticalSection(&cs);
 ...
 //   操作dwTime
 ...
 LeaveCriticalSection(&cs);
 return   0;
 }
 
 这样,当线程ThreadFuncB醒过来时,它遇到的第一个语句是EnterCriticalSection(&cs),这个语句将对cs变量进行访问。如果这个时候第一个线程仍然在操作dwTime[100],cs变量中包含的值将告诉第二个线程,已有其它线程占用了cs。因此,第二个线程的 EnterCriticalSection(&cs)语句将不会返回,而处于挂起等待状态。直到第一个线程执行了 LeaveCriticalSection(&cs),第二个线程的EnterCriticalSection(&cs)语句才会返回,并且继续执行下面的操作。
 
 这个过程实际上是通过限制有且只有一个函数进入CriticalSection变量来实现代码段同步的。简单地说,对于同一个 CRITICAL_SECTION,当一个线程执行了EnterCriticalSection而没有执行LeaveCriticalSection的时候,其它任何一个线程都无法完全执行EnterCriticalSection而不得不处于等待状态。
 
 再次强调一次,没有任何资源被“锁定”,CRITICAL_SECTION这个东东不是针对于资源的,而是针对于不同线程间的代码段的!我们能够用它来进行所谓资源的“锁定”,其实是因为我们在任何访问共享资源的地方都加入了EnterCriticalSection和 LeaveCriticalSection语句,使得同一时间只能够有一个线程的代码段访问到该共享资源而已(其它想访问该资源的代码段不得不等待)。
 
 这就是使用一个CRITICAL_SECTION时的情况。你应该要知道,它并没有什么可以同步的资源的“集合”。这个概念不正确。
 
 如果是两个CRITICAL_SECTION,就以此类推。
 | 
    
 
虽然临界区同步速度很快,但却只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。
    MFC提供了很多功能完备的类,我用MFC实现了临界区。MFC为临界区提供有一个CCriticalSection类,使用该类进行线程同步处理是非常简单的。只需在线程函数中用CCriticalSection类成员函数Lock()和UnLock()标定出被保护代码片段即可。Lock()后代码用到的资源自动被视为临界区内的资源被保护。UnLock后别的线程才能访问这些资源。
	posted on 2010-07-08 10:39 
风轻云淡 阅读(1863) 
评论(0)  编辑 收藏 引用  所属分类: 
C++