即互斥对象、事件目标和重大代码段,关键代码段(临界区)是指一个小代码段

可以从例子学习,更好的控制

二种线程同步格局,即互斥对象、事件目标和首要代码段。

 

一、互斥对象

1、属于基本对象,它可以保障线程拥有对单个能源的排外访问权。互斥对象涵盖一个行使数据,一个线程ID和一个计数器。ID用于标识系统中的哪个线程当前拥有互斥对象,计数器用于指明该线程拥有互斥对象的次数。

      2、函数:CreateMutex

          函数原型:HANDLE
CreateMutex( LPSECU奥迪Q7ITY_ATTRIBUTES lpMutexAttributes, BOOL
bInitialOwner, LPCTSTR lpName);
         
该函数可以创制或打开一个命名的或匿名的排外对象,然后程序就足以应用该互斥对象完毕线程间的一块儿。
          函数参数:
       
 <1>lpMutexAttributes:值为NULL时,让互斥对象使用暗中认同的安全性。
       
 <2>bInitialOwner:若值为真,则创立这几个互斥对象的线程得到该对象的所有权;否则,该线程将不获取所创设的排斥对象的所有权。
       
 <3>lpName:指定互斥对象的名称。如若值为NULL,则创立一个匿名的排斥对象。

       
 PS:假诺该函数调用成功,将重返所创立的排挤对象的句柄。如若创制的是命名的排外对象,并且在CreateMutex函数调用此前,该命名的排挤对象存在,将回来已经存在的这几个互斥对象的句柄,而此刻调用GetLastError函数,将回到E索罗德RO奥迪Q5_ALREADY_EXISTS。当线程截至对互斥对象的造访后,应释放该目的的所有权,需调用ReleaseMutex函数落成。函数原型为:BOOL
ReleaseMutex( HANDLE hMutex);调用成功重临非0值,否则再次来到0。

       
 线程必须主动请求共享对象的使用权才有大概取得该所有权。要求使用函数WaitForSingleObject达成,函数解释见http://blog.csdn.net/sharing_li/article/details/38874777 

      3、示例代码

#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;                                                   

}

二、事件目标

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

        1、使用计数;

       
2、用于指明该事件是一个活动重置的风云依然一个人工重置事件的布尔值;

       
3、用于指明该事件高居已文告状态如故未通报状态的布尔值。

       
当人工重置的风云目的拿到关照时,等待该事件目标的拥有线程均成为可调度线程。当一个电动重置的轩然大波目的得到关照时,等待该事件目的的线程中唯有一个线程变为可调度线程。此外,自动重置事件拿到关照并被线程拥有之后,操作系统会将该事件目的设置为无信号状态,那样,当对所保证的代码执行完结之后,必要调用Set伊芙nt函数将该事件目的设置为有信号状态。而人工重置事件拿到关照并被线程拥有之后,操作系统并不会将该事件设置为无信号状态,除非展现地调用Reset伊芙nt函数将其安装为无信号状态。为了落到实处线程间的一块,不应有利用人工重置的风云目标,而应当接纳机关重置的轩然大波目的。

        函数:CreateEvent

        函数原型:HANDLE
Create伊夫nt(LPSECURAV4ITY_ATTRIBUTES
lpEventAttributes, 

BOOL bManualReset, BOOL bInitialState,
LPCTSTR lpName);

     
 Create伊芙nt函数创造或打开一个命名的或匿名的风云目的。

       函数参数:

       <1>lpEventAttributes:指向SECURITY_ATT酷威IBUTES结构体的指针。借使值为NULL,则使用暗中认可的安全性。

<2>bManualReset:尽管值为TRUE,表示该函数将创立一个人造重置事件目标;如果值为FALSE,

意味着该函数将开创一个机关重置事件目标。

<3>bInitialState:假若值为TRUE,那么该事件目的伊始是有信号状态;否则是无信号状态。

<4>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、InitializeCriticalSection(LPCRITICAL_SECTION  lpCriticalSection);

     
 2、DeleteCriticalSection(LPCRITICAL_SECTION  lpCriticalSection);

     
 3、EnterCriticalSection(LPCRITICAL_SECTION  lpCriticalSection);

     
 4、LeaveCriticalSection(LPCRITICAL_SECTION  lpCriticalSection);

     
使用这几个函数从前需构造一个C奔驰M级ITICAL_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;
}

 

四、比较

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

     
2、关键代码段工作在用户方式下,同步速度较快,但在利用主要代码段时,很不难进入死锁状态,因为在守候进入第一代码段时不能设定超时值。

     
3、常常,在编制二十四线程程序并必要贯彻线程同步时,首选关键代码段,它的采纳相比简单。当使用了多少个临界区目的,就要注意防范线程死锁的发生。假如急需在多少个进程间的各样线程间落成同台的话,可以接纳互斥对象和事件目的。

线程死锁现象示例代码:

#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
关键代码段(临界区)是指一个小代码段,在代码够执行前。它必须独占对少数能源的访问权。

3
关键代码段是工作在用户形式下。同步速度较快,但在应用主要代码段时,很不难进入死锁状态。因为在等候进入第一代码段时胸中无数设定超时值。

4 线程死锁 线程1拥有了临界区目的A。等待临界区目的B的拥有权。线程2独具了临界区目的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;                                                    
}

 

相关文章