重要代码段(临界区)是指1个小代码段,即互斥对象、事件目的和严重性代码段

能够从例子学习,更加好的牵线

三种线程同步方式,即互斥对象、事件目的和根本代码段。

 

一、互斥对象

1、属于基本对象,它亦可确认保障线程具备对单个财富的排斥访问权。互斥对象涵盖贰个运用数据,1个线程ID和3个计数器。ID用于标志系统中的哪个线程当前抱有互斥对象,计数器用于指明该线程具有互斥对象的次数。

      2、函数:CreateMutex

          函数原型:HANDLE
CreateMutex( LPSECULacrosseITY_ATTRIBUTES lpMutexAttributes, BOOL
bInitialOwner, LPCTSTR lpName);
         
该函数能够创造或展开三个命名的或匿名的排挤对象,然后程序就能够利用该互斥对象达成线程间的一同。
          函数参数:
       
 <一>lpMutexAttributes:值为NULL时,让互斥对象使用私下认可的安全性。
       
 <2>bInitialOwner:若值为真,则成立这些互斥对象的线程获得该指标的全数权;不然,该线程将不到手所创办的排挤对象的全体权。
       
 <三>lpName:钦定互斥对象的名目。若是值为NULL,则开创一个匿名的排挤对象。

       
 PS:借使该函数调用成功,将回到所创建的排挤对象的句柄。假设创造的是命名的排外对象,并且在CreateMutex函数调用从前,该命名的排挤对象存在,将回到已经存在的这一个互斥对象的句柄,而此时调用GetLastError函数,将赶回E逍客ROXC90_ALREADY_EXISTS。当线程截至对互斥对象的造访后,应释放该对象的全部权,需调用ReleaseMutex函数完成。函数原型为:BOOL
ReleaseMutex( HANDLE hMutex);调用成功再次回到非0值,不然重临0。

       
 线程必须积极请求共享对象的使用权才有相当大大概赢得该全体权。供给选取函数WaitForSingleObject达成,函数解释见http://blog.csdn.net/sharing_li/article/details/38874777 

      三、示例代码

#include <windows.h>
#include <iostream.h>

DWORD WINAPI Fun1Proc(LPVOID lpParameter  );
DWORD WINAPI Fun2Proc(LPVOID lpParameter  );
int index=0;
int tickets=100;
HANDLE hMutex;
void main()
{
    HANDLE hThread1;
    HANDLE hThread2;
    hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
    hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
    CloseHandle(hThread1);
    CloseHandle(hThread2);

    hMutex=CreateMutex(NULL,FALSE,NULL);//第二个参数为FALSE,表明无线程拥有这个互斥对象。

    Sleep(4000);
}

DWORD WINAPI Fun1Proc( LPVOID lpParameter )
{
    while(TRUE)
    {
        WaitForSingleObject(hMutex,INFINITE);
        if(tickets>0)
        {
            Sleep(1);
            cout<<"thread1 sell ticket : "<<tickets--<<endl;
        }
        else
            break;
        ReleaseMutex(hMutex);
    }
    return 0;
}

DWORD WINAPI Fun2Proc(LPVOID lpParameter )
{

    while(TRUE)
    {
        WaitForSingleObject(hMutex,INFINITE);
        if(tickets>0)
        {
            Sleep(1);
            cout<<"thread2 sell ticket : "<<tickets--<<endl;
        }
        else
            break;
        ReleaseMutex(hMutex);
    }
    return 0;
}
#include <windows.h>                                      
#include <iostream.h>                                    
//两个线程的声明
DWORD WINAPI Fun1Proc(LPVOID lpParameter);               
DWORD WINAPI Fun2Proc(LPVOID lpParameter);               

int tickets = 100;
CRITICAL_SECTION g_cs;              //定义一个临界区                                           

int main()                                              
{
       HANDLE hThread1;                                     
       HANDLE hThread2;                                     
       hThread1=CreateThread(NULL,0, Fun1Proc, NULL,0, NULL);  
       hThread2=CreateThread(NULL,0, Fun2Proc, NULL,0, NULL);   
       CloseHandle(hThread1);                                
       CloseHandle(hThread2);

       InitializeCriticalSection(&g_cs);     //初始化临界区
       Sleep(4000);

       DeleteCriticalSection(&g_cs);        //当临界区里没有资源时释放掉临界区(可以这样说吗)???

       return 0;
}


DWORD WINAPI Fun1Proc(LPVOID lpParameter)                
{                                                               


       while (TRUE)                                         
       {
           EnterCriticalSection(&g_cs);          //该线程进入临界区   
              if (tickets > 0)
              {
                     Sleep( 1);                                     
                     cout<< "Thread1 sell tickets:"<< tickets-- << endl;
              }
              else
              {
                     break;
              }
              LeaveCriticalSection(&g_cs);        //退出临界区
       }

return0;                                             
}                                                         


DWORD WINAPI Fun2Proc(LPVOID lpParameter)                
{

       while(TRUE)                                          
       {
               EnterCriticalSection(&g_cs);       //该线程进入临界区     
              if(tickets > 0)
              {
                     Sleep( 1);                                    
                     cout<< "Thread2 sell tickets:"<< tickets-- << endl;
              }
              else
              {
                     break;
              }
              LeaveCriticalSection(&g_cs);           //退出临界区                                                         
       }
       return 0;                                                   

}

2、事件目的

        属于基本对象,包括多个成员:

        一、使用计数;

       
2、用于指明该事件是贰个机动复位的风云或许一人造重新载入参数事件的布尔值;

       
叁、用于指明该事件高居已通报状态依然未文告状态的布尔值。

       
当人工重新恢复设置的轩然大波目的获得关照时,等待该事件目的的具备线程均成为可调度线程。当四个机动重新初始化的风云指标得到文告时,等待该事件指标的线程中唯有一个线程变为可调度线程。其余,自动重新初始化事件获得文告并被线程具有之后,操作系统会将该事件指标设置为无复信号状态,那样,当对所保证的代码试行达成之后,必要调用Set伊夫nt函数将该事件目的设置为有功率信号状态。而人工重新设置事件获得关照并被线程具备之后,操作系统并不会将该事件设置为无时限信号状态,除非显示地调用Reset伊芙nt函数将其安装为无确定性信号状态。为了兑现线程间的一块儿,不应该利用人工重新恢复设置的风浪指标,而相应运用电动重新载入参数的轩然大波目的。

        函数:CreateEvent

        函数原型:HANDLE
CreateEvent(LPSECU帕杰罗ITY_ATTRIBUTES
lpEventAttributes, 

BOOL bManualReset, BOOL bInitialState,
LPCTSTR lpName);

     
 Create伊夫nt函数成立或张开三个命名的或匿名的风云目的。

       函数参数:

       <1>lpEventAttributes:指向SECURITY_ATT帕杰罗IBUTES结构体的指针。假设值为NULL,则动用暗中同意的安全性。

<二>b马努alReset:若是值为TRUE,表示该函数将创立1个人工重新恢复设置事件指标;假若值为FALSE,

意味着该函数将创立一个机关心珍视置事件目的。

<三>bInitialState:假设值为TRUE,那么该事件指标发轫是有连续信号状态;不然是无时限信号状态。

<四>lpName:假设此参数为NULL,将创立一个匿名的轩然大波目的。

安装和重新载入参数事件目的景况函数:Set伊夫nt(HANDLE
h伊芙nt);把钦赐的风云指标设置为有时域信号状态。Reset伊芙nt(HANDLE
h伊芙nt);把内定的轩然大波目的设置为无时限信号状态。

  实例代码:

#include <windows.h>
#include <iostream.h>

DWORD WINAPI Fun1Proc( LPVOID lpParameter );
DWORD WINAPI Fun2Proc( LPVOID lpParameter);

int tickets=100;
HANDLE g_hEvent;

void main()
{
    HANDLE hThread1;
    HANDLE hThread2;
    hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
    hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
    CloseHandle(hThread1);
    CloseHandle(hThread2);

    g_hEvent=CreateEvent(NULL,FALSE,TRUE,NULL);

    Sleep(4000);
    CloseHandle(g_hEvent);
}

DWORD WINAPI Fun1Proc(  LPVOID lpParameter )
{
    while(TRUE)
    {
        WaitForSingleObject(g_hEvent,INFINITE);
        if(tickets>0)
        {
            Sleep(1);
            cout<<"thread1 sell ticket : "<<tickets--<<endl;
        }
        else
            break;
        SetEvent(g_hEvent);
    }

    return 0;
}

DWORD WINAPI Fun2Proc( LPVOID lpParameter)
{

    while(TRUE)
    {
        WaitForSingleObject(g_hEvent,INFINITE);
        if(tickets>0)
        {
            Sleep(1);
            cout<<"thread2 sell ticket : "<<tickets--<<endl;
        }
        else
            break;
        SetEvent(g_hEvent);
    }

    return 0;
}

 

叁、关键代码段

       也称为临界区,工作在用户形式下。它是指三个小代码段,在代码能够施行前,它必须独占对少数财富的访问权。平常把多线程中访问同1种资源的那某个代码当作重中之重代码段。

       相关函数:

     
 1、InitializeCriticalSection(LPCRITICAL_SECTION  lpCriticalSection);

     
 2、DeleteCriticalSection(LPCRITICAL_SECTION  lpCriticalSection);

     
 3、EnterCriticalSection(LPCRITICAL_SECTION  lpCriticalSection);

     
 4、LeaveCriticalSection(LPCRITICAL_SECTION  lpCriticalSection);

     
使用这个函数在此以前需构造一个CPRADOITICAL_SECTION结构体类型的对象,然后将该对象传递给函数,系统活动保养该对象。

      示例代码:

#include <windows.h>
#include <iostream.h>

DWORD WINAPI Fun1Proc( LPVOID lpParameter );
DWORD WINAPI Fun2Proc( LPVOID lpParameter );

int tickets=100;

CRITICAL_SECTION g_cs;

void main()
{
    HANDLE hThread1;
    HANDLE hThread2;
    hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
    hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
    CloseHandle(hThread1);
    CloseHandle(hThread2);

    InitializeCriticalSection(&g_cs);
    Sleep(4000);

    DeleteCriticalSection(&g_cs);
}

DWORD WINAPI Fun1Proc( LPVOID lpParameter )
{
    while(TRUE)
    {
        EnterCriticalSection(&g_cs);
        Sleep(1);
        if(tickets>0)
        {
            Sleep(1);
            cout<<"thread1 sell ticket : "<<tickets--<<endl;
        }
        else
            break;
        LeaveCriticalSection(&g_cs);
    }

    return 0;
}

DWORD WINAPI Fun2Proc( LPVOID lpParameter )
{
    while(TRUE)
    {
        EnterCriticalSection(&g_cs);
        Sleep(1);
        if(tickets>0)
        {
            Sleep(1);
            cout<<"thread2 sell ticket : "<<tickets--<<endl;
        }
        else
            break;
        LeaveCriticalSection(&g_cs);
    }
    return 0;
}

 

四、比较

     
壹、互斥对象和事件目的都属于基本对象,利用内核查象开始展览线程同步时,速度较慢,但使用互斥对象和事件目的那样的基本对象,能够在三个经过中的各样线程间展开联合。

     
二、关键代码段职业在用户方式下,同步速度较快,但在使用主要代码段时,很轻松进入死锁状态,因为在等候进入重点代码段时不能够设定超时值。

     
三、平常,在编辑十二线程程序并须要贯彻线程同步时,首荐关键代码段,它的利用比较轻便。当使用了四个临界区目的,就要专注防御线程死锁的发生。倘诺须求在多少个进度间的逐一线程间实现协同的话,能够行使互斥对象和事件指标。

线程死锁现象示例代码:

#include <windows.h>
#include <iostream.h>

DWORD WINAPI Fun1Proc( LPVOID lpParameter  );
DWORD WINAPI Fun2Proc( LPVOID lpParameter  );

int tickets=100;

CRITICAL_SECTION g_csA;
CRITICAL_SECTION g_csB;

void main()
{
    HANDLE hThread1;
    HANDLE hThread2;
    hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
    hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
    CloseHandle(hThread1);
    CloseHandle(hThread2);

    InitializeCriticalSection(&g_csA);
    InitializeCriticalSection(&g_csB);
    Sleep(4000);

    DeleteCriticalSection(&g_csA);
    DeleteCriticalSection(&g_csB);
}

DWORD WINAPI Fun1Proc(  LPVOID lpParameter )
{
    while(TRUE)
    {
        EnterCriticalSection(&g_csA);
        Sleep(1);
        EnterCriticalSection(&g_csB);
        if(tickets>0)
        {
            Sleep(1);
            cout<<"thread1 sell ticket : "<<tickets--<<endl;
        }
        else
            break;
        LeaveCriticalSection(&g_csB);
        LeaveCriticalSection(&g_csA);
    }

    return 0;
}

DWORD WINAPI Fun2Proc(  LPVOID lpParameter  )
{   
    while(TRUE)
    {
        EnterCriticalSection(&g_csB);
        Sleep(1);
        EnterCriticalSection(&g_csA);
        if(tickets>0)
        {
            Sleep(1);
            cout<<"thread2 sell ticket : "<<tickets--<<endl;
        }
        else
            break;
        LeaveCriticalSection(&g_csA);
        LeaveCriticalSection(&g_csB);
    }
    cout<<"thread2 is running!"<<endl;
    return 0;
}

参考《VC深切详解》

 

 应该注意的:

1 关键代码段(临界区)工作在用
用户格局下

2关键代码段(临界区)是指1个小代码段,在代码够实施前。它必须独占对一些能源的访问权。

3关键代码段是干活在用户格局下。同步速度较快,但在运用主要代码段时,很轻易进入死锁状态。因为在等待进入第二代码段时无法设定超时值。

四 线程死锁 线程一具有了临界区目的A。等待临界区指标B的具有权。线程二存有了临界区目的B,等待临界区目的A的具有权,就导致了死锁。

5 无法用它们对七个经过中的各种线程实行同步 

 

以下的例子就是进入了死锁。 

#include <windows.h>                                      
#include <iostream.h>                                    
//1、两个线程的声明
DWORD WINAPI Fun1Proc(LPVOID lpParameter);               
DWORD WINAPI Fun2Proc(LPVOID lpParameter);               

int tickets = 100;
CRITICAL_SECTION g_csA;              //2、定义两个临界区                                           
CRITICAL_SECTION g_csB;

int main()                                              
{
       HANDLE hThread1;                                      
       HANDLE hThread2;                                     
       hThread1=CreateThread(NULL,0, Fun1Proc, NULL,0, NULL);  
       hThread2=CreateThread(NULL,0, Fun2Proc, NULL,0, NULL);   
       CloseHandle(hThread1);                                 
       CloseHandle(hThread2);

       InitializeCriticalSection(&g_csA);     //3、初始化临界区
       InitializeCriticalSection(&g_csB);
       Sleep(4000);

       DeleteCriticalSection(&g_csA);       //4、当临界区里没有资源时释放掉临界区
    DeleteCriticalSection(&g_csB);
       return 0;
}


DWORD WINAPI Fun1Proc(LPVOID lpParameter)                
{                                                               

       // 11、两个临界区和两个线程会形成死机。线程在等待无法进入临界区g_csB。线程在等待无法进入临界区g_csA

       while (TRUE)                                         
       {
           EnterCriticalSection(&g_csA);          //5、该线程进入临界区g_csA    
              Sleep(1);                              //6、线程进入睡眠状态 线程时间片断结束线程开始          
EnterCriticalSection(&g_csB);         //9、线程进入临界区g_csB,但是临界区g_csB已被线程占用故只能等待时间片断交给线程    
              if (tickets > 0)
              {
                     Sleep( 1);                                      
                     cout<< "Thread1 sell tickets:"<< tickets-- << endl;
              }
              else
              {
                     break;
              }
              LeaveCriticalSection(&g_csB);        //退出临界区
              LeaveCriticalSection(&g_csA);
       }

       return 0;                                              
}                                                         


DWORD WINAPI Fun2Proc(LPVOID lpParameter)                
{

       while(TRUE)                                          
       {
              EnterCriticalSection(&g_csB);       //7、线程进入临界区g_csB
              Sleep(1);                           //8、线程进入睡眠状态线程时间片断结束线程开始           
EnterCriticalSection(&g_csA);       //10、线程进入临界区g_csA 但是临界区g_csA已被线程占用故只能等待时间片断交给线程          

              if(tickets > 0)
              {
                     Sleep( 1);                                    
                     cout<< "Thread2 sell tickets:"<< tickets-- << endl;
              }
              else
              {
                     break;
              }
              LeaveCriticalSection(&g_csA);
              LeaveCriticalSection(&g_csB);        //退出临界区                                                         
       }
       return 0;                                                    
}

 

网站地图xml地图