Windows线程同步的四种方法

一、基于CRITICAL_SECTION的同步:

基于CRITICAL_SECTION的同步中将创建并运用"CRITICAL_SECTION对象“, 但这并非内核对象。与其他同步对象相同,它是进入临界区的一把”钥匙“。因此,为了进入临界区,需要得到CRITICAL_SECTION对象这把”钥匙“。离开时需要上交CRITICAL_SECTION对象。

    #include <windows.h>
    //初始化函数原型
    VOID InitializeCriticalSection(
      LPCRITICAL_SECTION lpCriticalSection   // address of critical 
                                             // section object
    );
    //销毁函数原型
    VOID DeleteCriticalSection(
      LPCRITICAL_SECTION lpCriticalSection   // pointer to critical 
                                             // section object
    );

其中lpCriticalSection, 在初始化函数中传入需要初始化的CRITICAL_SECTION对象的地址值,销毁函数中传入需要解除的CRITICAL对象的地址值。

销毁函数并不是销毁CRITICAL_SECTION对象的函数,该函数的作用是销毁CRITICAL_SECTION对象使用过的资源。

获取及释放CRITICAL_SECTION对象的函数:

    #include <window.h>
    //获取
    VOID EnterCriticalSection(
      LPCRITICAL_SECTION lpCriticalSection   // pointer to critical 
                                             // section object
    );
    //释放
    VOID LeaveCriticalSection(
      LPCRITICAL_SECTION lpCriticalSection   // address of critical 
                                             // section object
    );
    //lpCriticalSection 参数为获取和释放CRITICAL_SECTION对象的地址值。
示例程序:

    #include <windows.h>
    #include <stdio.h>
    #include <process.h>
    #include <stdlib.h>
    #define NUM_THREAD 50
    unsigned WINAPI threadInc(void * arg);
    unsigned WINAPI threadDes(void * arg);
    long long num = 0;
    CRITICAL_SECTION cs;

   int main()
    {
        HANDLE tHandles[NUM_THREAD];
        int i;
        InitializeCriticalSection(&cs);     //初始化临界区
        for(i = 0; i < NUM_THREAD; i++)
        {    
            if(i % 2)
                tHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadInc, NULL, 0, NULL);
            else
                tHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadDes, NULL, 0, NULL);
        }
        
        WaitForMultipleObjects(NUM_THREAD, tHandles, TRUE, INFINITE);
        DeleteCriticalSection(&cs);        //释放临界区
        printf("result: %lld \n", num);
        
        return 0;
    }
    unsigned WINAPI threadInc(void * arg)
    {
        int i;
        EnterCriticalSection(&cs);      //进入临界区
        for(i = 0; i < 50000000; i++)
            num += 1;
        LeaveCriticalSection(&cs);     //离开临界区
        return 0;
    }
    unsigned WINAPI threadDes(void * arg)
    {
        int i;
        EnterCriticalSection(&cs);      //进入临界区
        for(i = 0; i < 50000000; i++)
            num -= 1;
        LeaveCriticalSection(&cs);      //离开临界区
        return 0;
    }


二、基于互斥量对象的同步:

基于互斥量对象的同步方法与给予CRITICAL_SECTION对象的同步方法类似。

创建互斥量对象的函数:

    HANDLE CreateMutex(
      LPSECURITY_ATTRIBUTES lpMutexAttributes,
                           // pointer to security attributes
      BOOL bInitialOwner,  // flag for initial ownership
      LPCTSTR lpName       // pointer to mutex-object name
    );
     
    //参数意义:
    //lpMutextAttributes 传递安全相关的配置信息,使用默认安全设置时可以传递NULL
    //bInitialOwner 如果为TRUE,则创建出的互斥量对象属于调用该函数的线程,同时进入non-signaled状态;
    //              如果为FALSE,则创建出的互斥量对象不属于任何线程,此时状态为signaled
    //lpName 用于命名互斥量对象。传入NULL时创建无名的互斥量对象
可以看出,如果互斥量对象不属于任何拥有着,则将进入signaled状态,利用该特点进行同步。另外,互斥量属于内核对象,所以通过如下函数销毁:

BOOL CloseHandle(
  HANDLE hObject   //要销毁内核对象的句柄
);
获取和释放互斥量的函数:

//获取函数 windows线程创建中介绍的此函数,用于针对单个内核对象验证signaled。
DWORD WaitForSingleObject(
  HANDLE hHandle,        // handle to object to wait for
  DWORD dwMilliseconds   // time-out interval in milliseconds
);

//释放互斥量
BOOL ReleaseMutex(  
    HANDLE hMutex   //需要释放的对象的句柄
); 
互斥量被某一线程获取时为non-signaled状态,释放时进入signaled状态。因此,可以利用WaitForSingleObject函数验证互斥量是否已分配。

互斥量在WaitForSingleObject函数返回时自动进入non-signaled状态,因为它是"auto-reset"模式的内核对象。

    #include <windows.h>
    #include <process.h>
    #include <stdio.h>
    #include <stdlib.h>
    #define NUM_THREAD 50

   unsigned WINAPI threadInc(void * arg);
    unsigned WINAPI threadDes(void * arg);
  
   long long num = 0;
    HANDLE hMutex;

   int main()
    {
        HANDLE tHandles[NUM_THREAD];
        int i;
        hMutex = CreateMutex(NULL, FALSE, NULL);   //创建互斥量,此时为signaled状态
        for (i = 0; i < NUM_THREAD; i++)
        {
            if (i % 2)
                tHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadInc, NULL, 0, NULL);
            else
                tHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadDes, NULL, 0, NULL);
        }
        WaitForMultipleObjects(NUM_THREAD, tHandles, TRUE, INFINITE);
        CloseHandle(hMutex);                     //销毁对象
        printf("result: %lld \n", num);
        return 0;
    }
    unsigned WINAPI threadInc(void * arg)
    {
        int i;
        WaitForSingleObject(hMutex, INFINITE);    //获取,进入的钥匙
        for (i = 0; i < 50000000; i++)
            num += 1;
        ReleaseMutex(hMutex);                    //释放,离开时上交钥匙
        return 0;
    }

   unsigned WINAPI threadDes(void * arg)
    {
        int i;
        WaitForSingleObject(hMutex, INFINITE);   
        for (i = 0; i < 50000000; i++)
            num -= 1;
        ReleaseMutex(hMutex);
        return 0;
    }

三、基于信号量对象的同步:

创建与销毁函数:

//创建信号量对象
HANDLE CreateSemaphore(
  LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
                       // pointer to security attributes
  LONG lInitialCount,  // initial count
  LONG lMaximumCount,  // maximum count
  LPCTSTR lpName       // pointer to semaphore-object name
);

//参数意义:
//lpSemaphoreAttributes  安全配置信息,采用默认安全设置时NULL
//lInitialCount  指定信号量的初始值,应大于0小于lMaximumCount
//lMaximumCount  信号量的最大值。该值为1时,信号量变为只能表示0和1的二进制信号量
//lpName  用于命名信号量对象。传递NULL时创建无名的信号量对象
//销毁信号量同样使用CloseHandle()函数

可以利用“信号量值为0时进入non-signaled状态,大于0时进入signaled状态”的特性进行同步。向lInitialCount参数传递0时,创建non-signaled状态的信号量对象。而向lMaximumCount传入3时,信号量最大值为3,因此可以实现3个线程同时访问临界区时的同步。


释放信号量对象的函数:

//释放信号量
BOOL ReleaseSemaphore(
  HANDLE hSemaphore,   // handle to the semaphore object
  LONG lReleaseCount,  // amount to add to current count
  LPLONG lpPreviousCount   // address of previous count
);
//参数意义:
//hSemaphore  传递需要释放的信号量对象。
//lReleaseCount  释放以为着信号量值的增加,通过该参数可以指定增加的值。超过最大值则不增加,返回FALSE
//lpPreviousCount  用于保存之前值得变量地址,不需要是可传递NULL
信号量对象大于0时成为signaled对象,为0时成为non-signaled状态。因此,调用WaitForSingleObject函数时,信号量大于0的情况下才会返回。返回的同时将信号量的值减1,同时进入non-signaled状态。
    #include <windows.h>
    #include <process.h>
    #include <stdio.h>

   unsigned WINAPI Read(void * arg);
    unsigned WINAPI Accu(void * arg);

   static HANDLE semOne;
    static HANDLE semTwo;
    static int num;
  
   int main(int argc, char *argv[])
    {
        HANDLE hThread1, hThread2;
        //创建信号量对象,设置为0进入non-signaled状态
        semOne = CreateSemaphore(NULL, 0, 1, NULL);
        //创建信号量对象,设置为1进入signaled状态
        semTwo = CreateSemaphore(NULL, 1, 1, NULL);
        hThread1 = (HANDLE)_beginthreadex(NULL, 0, Read, NULL, 0, NULL);
        hThread2 = (HANDLE)_beginthreadex(NULL, 0, Accu, NULL, 0, NULL);
        WaitForSingleObject(hThread1, INFINITE);
        WaitForSingleObject(hThread2, INFINITE);
        CloseHandle(semOne); //销毁
        CloseHandle(semTwo); //销毁
        return 0;
    }
    unsigned WINAPI Read(void * arg)
    {
        int i;
        for (i = 0; i < 5; i++)
        {
            fputs("Input num: ", stdout);
            //临界区的开始 signaled状态
            WaitForSingleObject(semTwo, INFINITE);
            scanf("%d", &num);
            //临界区的结束 non-signaled状态
            ReleaseSemaphore(semOne, 1, NULL);
        }
        return 0;
    }
    unsigned WINAPI Accu(void * arg)
    {
        int sum = 0, i;
        for (i = 0; i < 5; i++)
        {
            //临界区的开始 non-signaled状态
            WaitForSingleObject(semOne, INFINITE);
            sum += num;
            //临界区的结束 signaled状态
            ReleaseSemaphore(semTwo, 1, NULL);
        }
        printf("Result: %d \n", sum);
        return 0;
    

四、基于事件对象的同步:

事件同步对象与前2种同步方法相比有很大不同,区别在于:该方式下创建对象时,可以在自动non-signaled状态运行的auto-reset模式和与之相反的manual-reset模式中任选其一。而事件对象的主要特点是可以创建manual-reset模式的对象。

创建事件对象的函数:
HANDLE CreateEvent(
  LPSECURITY_ATTRIBUTES lpEventAttributes,
                      // pointer to security attributes
  BOOL bManualReset,  // flag for manual-reset event
  BOOL bInitialState, // flag for initial state
  LPCTSTR lpName      // pointer to event-object name
);
 
参数说明:
//lpEventAttributes  安全配置相关参数,采用默认安全配置时传入NULL
//bManualReset  传入TRUE时创建manual-reset模式的事件对象,传入FALSE时创建auto-reset模式的事件对象
//bInitialState  传入TRUE时创建signaled状态,传入FALSE时创建non-signaled状态的事件对象
//lpName  用于命名事件对象。传递NULL时创建无名的事件对象
当第二个参数传入TRUE时将创建manual-reset模式的事件对象,此时即使WaitForSingleObject函数返回也不会回到non-signaled状态。因此,在这种情况下,需要通过如下2个函数明确更改对象状态。
    BOOL ResetEvent(
      HANDLE hEvent   // to the non-signaled
    );
     
    BOOL SetEvent(
      HANDLE hEvent   // to the signaled
    );

传递事件对象句柄并希望改为non-signed状态时,应调用ResetEvent函数。如果希望改为signaled状态,则可以调用SetEvent函数。


实例程序:

    #include <windows.h>
    #include <stdio.h>
    #include <process.h>
    #define STR_LEN    100
     
    unsigned WINAPI NumberOfA(void *arg);
    unsigned WINAPI NumberOfOthers(void *arg);
     
    static char str[STR_LEN];
    static HANDLE hEvent;
     
    int main(int argc, char *argv[])
    {
        HANDLE hThread1, hThread2;
     
        //以non-signaled创建manual-reset模式的事件对象
        hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
     
        hThread1 = (HANDLE)_beginthreadex(NULL, 0, NumberOfA, NULL, 0, NULL);
        hThread2 = (HANDLE)_beginthreadex(NULL, 0, NumberOfOthers, NULL, 0, NULL);
     
        fputs("Input string: ", stdout);
        fgets(str, STR_LEN, stdin);
     
        //读入字符串后改为signaled状态
        SetEvent(hEvent);           
     
        WaitForSingleObject(hThread1, INFINITE);
        WaitForSingleObject(hThread2, INFINITE);
     
        //non-signaled 如果不更改,对象继续停留在signaled
        ResetEvent(hEvent);         
        CloseHandle(hEvent);
     
        return 0;
    }
     
    unsigned WINAPI NumberOfA(void *arg)
    {
        int i, cnt = 0;
        WaitForSingleObject(hEvent, INFINITE);
        for (i = 0; str[i] != 0; i++)
        {
            if (str[i] == 'A')
                cnt++;
        }
        printf("Num of A: %d \n", cnt);
        return 0;
    }
     
    unsigned WINAPI NumberOfOthers(void *arg)
    {
        int i, cnt = 0;
        WaitForSingleObject(hEvent, INFINITE);
        for (i = 0; str[i] != 0; i++)
        {
            if (str[i] != 'A')
                cnt++;
        }
        printf("Num of others: %d \n", cnt - 1);
        return 0;
    }


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值