zoukankan      html  css  js  c++  java
  • Windows 临界区CRITICAL_SECTION,内核事件Event,互斥量Mutex,信号量Semaphore

    http://www.cnblogs.com/vieri122/archive/2009/09/10/1564104.html

    Windows 临界区,内核事件,互斥量,信号量。

    临界区,内核事件,互斥量,信号量,都能完成线程的同步,在这里把他们各自的函数调用,结构定义,以及适用情况做一个总结。

    . 临界区 CRITICAL_SECTION:

    适用范围:它只能同步一个进程中的线程,不能跨进程同步。一般用它来做单个进程内的代码快同步,效率比较高。

    相关结构:CRITICAL_SECTION _critical

    相关方法:

    // 初始化,最先调用的函数。一般windows编程都有类似初始化的方法

    InitializeCriticalSection(& _critical)

    // 释放资源,确定不使用_critical时调用,一般在程序退出的时候调用。

    // 如果以后还要用_critical,则要重新调用InitializeCriticalSection

    DeleteCriticalSection(& _critical)

    // 把代码保护起来。调用此函数后,他以后的资源其他线程就不能访问了。

    EnterCriticalSection& _critical

    // 离开临界区,表示其他线程能够进来了。

    // 注意EnterCriticalLeaveCrticalSection必须是成对出现的!当然除非你是想故意死锁!

    LeaveCriticalSection(& _critical)

    例子:

    临界区

    #include "stdafx.h"

    int thread_count = 0;

    /*Mutex mutex1;*/

    CRITICAL_SECTION g_cs;

    DWORD CALLBACK thread_proc(LPVOID params)

    {

    for(int i = 0; i < 10; ++i)

    {

    //synchronized(mutex1)

    EnterCriticalSection(&g_cs);

    {

    for(char c = 'A'; c <= 'Z'; ++c)

    {

    printf("%c",c);

    }

    printf("/n");

    }

    LeaveCriticalSection(&g_cs);

    }

    thread_count--;

    return 0;

    }

    int _tmain(int argc, _TCHAR* argv[])

    {

    InitializeCriticalSection(&g_cs);

    thread_count = 4;

    CreateThread(0, 0, thread_proc, 0, 0, 0);

    CreateThread(0, 0, thread_proc, 0, 0, 0);

    CreateThread(0, 0, thread_proc, 0, 0, 0);

    CreateThread(0, 0, thread_proc, 0, 0, 0);

    while (thread_count)

    Sleep(0);

    getchar();

    DeleteCriticalSection(&g_cs);

    return 0;

    . 内核事件Event

    适用范围:多用于线程间的通信,可以跨进程同步。

    相关结构: HANDLE hEvent;

    相关方法:

    // 初始化方法,创建一个事件对象,第一个参数表示安全属性,一般情况下,

    // 遇到这类型的参数直接给空就行了,第二个参数是否是人工重置。(内核时间有两种工作模式:

    // 人工重置和自动重置。其区别会在下面提到。)。第三个参数是初始状态,第四个参数事件名称。

    hEvent = CreateEventNULL,FALSE,FALSE,NULL);

    // 等待单个事件置位,即线程会在这个函数阻塞直到事件被置位,SetEvent

    // 如果是自动重置事件,则在此函数返返回后系统会自动调用ResetEventhEvnet),

    // 重置事件,保证其他线程不能访问。

    // 如果是人工重置事件,则在此函数返回以后,系统的其他线程能继续访问。

    // 第二个参数说明等待事件,INIFINET表示一直等待。

    WatiForSingleObject(hEvent, INIFINET)

    // 置位事件,只要使事件置位线程才能进去访问。即WatiForSingleObject(hEvent, INIFINET)才返回

    SerEvent(hEvent);

    // 重置事件,使得WatiForSingleObject()不返回

    ResetEvent(hEvent)

    // 等待多个事件对象。参数nCount指定了要等待的内核对象的数目,存放这些内核对象的数组由lpHandles

    // 来指向。fWaitAll对指定的这nCount个内核对 象的两种等待方式进行了指定,

    // TRUE时当所有对象都被通知时函数才会返回,为FALSE则只要其中任何一个得到通知就可以返回。

    // dwMilliseconds在这里的作用与在WaitForSingleObject()中的作用是完全一致的。如果等待超时,

    // 函数将返回 WAIT_TIMEOUT。如果返回WAIT_OBJECT_0WAIT_OBJECT_0+nCount-1中的某个值,

    // 则说明所有指定对象的状态均 为已通知状态(当fWaitAllTRUE时)或是用以减去WAIT_OBJECT_0

    // 而得到发生通知的对象的索引(当fWaitAllFALSE 时)

    WaitForMultiObjects(

    DWORD nCount, // 等待句柄数

      CONST HANDLE *lpHandles, // 句柄数组首地址

    BOOL fWaitAll, // 等待标志

    DWORD dwMilliseconds // 等待时间间隔

    )

    // 打开一个命名的事件对象,可以用来跨进程同步

    HANDLE OpenEvent(

    DWORD dwDesiredAccess, // 访问标志

    BOOL bInheritHandle, // 继承标志

    LPCTSTR lpName // 指向事件对象名的指针

    );

    测试代码

    内核事件

    #include "stdafx.h"

    /*#include "Mutex.h"*/

    int thread_count = 0;

    /*Mutex mutex1;*/

    /*CRITICAL_SECTION g_cs;*/

    HANDLE hEvent;

    DWORD CALLBACK thread_proc(LPVOID params)

    {

    for(int i = 0; i < 10; ++i)

    {

    //synchronized(mutex1)

    //EnterCriticalSection(&g_cs);

    WaitForSingleObject(hEvent,INFINITE);

    {

    for(char c = 'A'; c <= 'Z'; ++c)

    {

    printf("%c",c);

    Sleep(1);

    }

    printf("/n");

    }

    SetEvent(hEvent);

    //LeaveCriticalSection(&g_cs);

    }

    thread_count--;

    return 0;

    }

    int _tmain(int argc, _TCHAR* argv[])

    {

    //InitializeCriticalSection(&g_cs);

    hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);

    SetEvent(hEvent);

    thread_count = 4;

    CreateThread(0, 0, thread_proc, 0, 0, 0);

    CreateThread(0, 0, thread_proc, 0, 0, 0);

    CreateThread(0, 0, thread_proc, 0, 0, 0);

    CreateThread(0, 0, thread_proc, 0, 0, 0);

    while (thread_count)

    Sleep(0);

    getchar();

    //DeleteCriticalSection(&g_cs);

    return 0;

    }

    . 互斥量Mutex

    适用范围:可以跨进程同步,还可以用来保证程序只有一个实例运行(创建命名互斥量),也可以用来做线程间的同步

    相关结构:HANDLE hMutex;

    相关方法:

    // 创建互斥量,初始化的工作

    // 参数一为安全选项,一般为空

    // 参数二表示当前互斥量是否属于某个线程,一般为空

    // 参数三互斥量的名称,如果需要跨进程同步或者需要保证程序只有一个实例运行,

    // 则需要设置,其他情况一般为空。

    CreateMutex(NULL,FALSE,NULL)

    WaitForSingleObject(hMutex, INIFINET); // 同事件对象

    // 释放互斥量,以使得其他线程可以访问。

    ReleaseMutex(hMutex)

    // 在互斥对象通知引 起调用等待函数返回时,等待函数的返回值不再是通常的

    // WAIT_OBJECT_0(对于WaitForSingleObject()函数)或是在

    // WAIT_OBJECT_0WAIT_OBJECT_0+nCount-1之间的一个值(对于WaitForMultipleObjects()函 数),

    // 而是将返回一个WAIT_ABANDONED_0(对于WaitForSingleObject()函数)

    // 或是在WAIT_ABANDONED_0 WAIT_ABANDONED_0+nCount-1之间的一个

    // 值(对于WaitForMultipleObjects()函数)。

    WaitForMultiObjects(

    DWORD nCount, // 等待句柄数

      CONST HANDLE *lpHandles, // 句柄数组首地址

    BOOL fWaitAll, // 等待标志

      DWORD dwMilliseconds // 等待时间间隔

    )

    // 打开一个已经创建好了的命名互斥量,用于跨进程同步

    HANDLE OpenMutex(

    DWORD dwDesiredAccess, // 访问标志

    BOOL bInheritHandle, // 继承标志

    LPCTSTR lpName // 互斥对象名

    );

    测试demo

    互斥量

    #include "stdafx.h"

    /*#include "Mutex.h"*/

    int thread_count = 0;

    /*Mutex mutex1;*/

    /*CRITICAL_SECTION g_cs;*/

    //HANDLE hEvent;

    HANDLE hMutex;

    DWORD CALLBACK thread_proc(LPVOID params)

    {

    for(int i = 0; i < 10; ++i)

    {

    //synchronized(mutex1)

    //EnterCriticalSection(&g_cs);

    WaitForSingleObject(hMutex,INFINITE);

    //WaitForSingleObject(hEvent,INFINITE);

    //{

    for(char c = 'A'; c <= 'Z'; ++c)

    {

    printf("%c",c);

    Sleep(1);

    }

    printf("/n");

    //}

    //SetEvent(hEvent);

    ReleaseMutex(hMutex);

    //LeaveCriticalSection(&g_cs);

    }

    thread_count--;

    return 0;

    }

    int _tmain(int argc, _TCHAR* argv[])

    {

    //InitializeCriticalSection(&g_cs);

    //hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);

    //SetEvent(hEvent);

    hMutex = CreateMutex(NULL,FALSE,NULL);

    thread_count = 4;

    CreateThread(0, 0, thread_proc, 0, 0, 0);

    CreateThread(0, 0, thread_proc, 0, 0, 0);

    CreateThread(0, 0, thread_proc, 0, 0, 0);

    CreateThread(0, 0, thread_proc, 0, 0, 0);

    while (thread_count)

    Sleep(0);

    getchar();

    //DeleteCriticalSection(&g_cs);

    return 0;

    }

    . 信号量Semaphore

    HANDLE CreateSemaphore(
    LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
    LONG lInitialCount,
    LONG lMaximumCount,
    LPCTSTR lpName
    );


    CreateSemaphore是创建信号量。CreateSemaphore 创建一个信号量对象,在输入参数中指定初值和最大值,返回对象句柄
    返回值
    Long,如执行成功,返回信号机对象的句柄;零表示出错。会设置GetLastError。即使返回一个有效的句柄,但倘若它指出同名的一个信号机已经存在,那么GetLastError也会返回ERROR_ALREADY_EXISTS
    参数:
    lpSemaphoreAttributes是信号量的安全属性。
    lInitialCount是初始化的信号量。
    lMaximumCount是允许信号量增加到最大值。
    lpName是信号量的名称
    具体:
    lpSemaphoreAttributes SECURITY_ATTRIBUTES,指定一个SECURITY_ATTRIBUTES结构,或传递零值(将参数声明为ByVal As Long,并传递零值)——表示采用不允许继承的默认描述符。该参数定义了信号机的安全特性
    lInitialCount Long,设置信号机的初始计数。可设置零到lMaximumCount之间的一个值
    lMaximumCount Long,设置信号机的最大计数
    lpName String,指定信号机对象的名称。用vbNullString可创建一个未命名的信号机对象。如果已经存在拥有这个名字的一个信号机,就直接打开现成的信号机。这个名字可能不与一个现有的互斥体、事件、可等待计时器或文件映射的名称相符
    注解
    一旦不再需要,一定记住用CloseHandle关闭信号机的句柄。它的所有句柄都关闭以后,对象自己也会删除
    一旦值大于零,信号机就会触发(发出信号)。ReleaseSemaphore函数的作用是增加信号机的计数。如果成功,就调用信号机上的一个等待函数来减少它的计数

    OpenSemaphore: 打开并返回一个已存在的信号量对象句柄,用于后续访问
    参数:
    DWORD dwDesiredAccess, // 操作标志位,一般为SEMAPHORE_ALL_ACCESS
    BOOL bInheritHandle, // 继承标志位,一般为FALSE
    LPCTSTR lpName // 信号量对象名称


    ReleaseSemaphore是增加信号量。释放对信号量对象的占用,使之成为可用。
    参数:
    hSemaphore是要增加的信号量句柄。
    lReleaseCount是增加的计数,释放的个数。
    lpPreviousCount是增加前的数值返回; 前一个计数的地址,一般为NULL

    WaitForSingleObjects可在指定的时间内等待指定对象为可用状态,等待操作
    参数:
    HANDLE hHandle, // 等待的信号量的句柄
    DWORD dwMilliseconds // 等待的时间,以毫秒为单位,如果永久等待,则为INFINITE

    注意事项:所有的同步操作的必须成对存在,即锁一对象,一定要释放一个对象。但是如果在保护的代码快中发生异常,程序流程发生意外跳转而没有释放锁对象,导致程序进入死锁。所以在程序中必要的异常处理是必须的,但是C++中没有finally这样的关键字来保证不管是否发生异常都会执行的代码快。那怎么办呢?这就需要对C++的异常加一些小技巧来处理了......


    --
    ------------------------------------------------------------------------华丽分割线--------------------------------------------------------------------------------

    线程同步的三种方式(Mutex,Event,Critical
    Section)


     

    线程同步的三种方式:


    互斥对象


    事件对象


    关键代码段


    三者的比较:


    n      
    互斥对象和事件对象属于内核对象,利用内核对象进行线程同步,速度较慢,但利用互斥对象和事件对象这样的内核对象,可以在多个进程中的各个线程间进行同步。


    n      
    关键代码段是工作在用户方式下,同步速度较快,但在使用关键代码段时,很容易进入死锁状态,因为在等待进入关键代码段时无法设定超时值。


    互斥对象:


    HANDLE hMutex;               
    //
    全局变量


    hMutex=CreateMutex(NULL,TRUE,NULL);


    ReleaseMutex(hMutex);


           //在其他的线程中


           WaitForSingleObject(hMutex,INFINITE);


           需要同步的,访问共享资源的程序代码


           ReleaseMutex(hMutex);


    CloseHandle(g_hEvent);


    事件对象:


                  HANDLE g_hEvent;                   
    //全局变量


                 
    g_hEvent=CreateEvent(NULL,FALSE,FALSE,"tickets");


                  SetEvent(g_hEvent);


           //在其他的线程中


                        
    WaitForSingleObject(g_hEvent,INFINITE);


                        
    需要同步的,访问共享资源的程序代码


                         SetEvent(g_hEvent);     
    //置事件为有信号状态(其他线程可以访问)


                                                           
    //ResetEvent(g_hEvent)


    //置事件为无信.//号状态(其他线程不可访问,只在该//线程内有效)


                  CloseHandle(g_hEvent);


    关键代码段:


                  CRITICAL_SECTION g_cs;              
    //全局变量


                 
    InitializeCriticalSection(&g_cs);          
    //通常在类的构造函数中


           //在其他的线程中


                        
    EnterCriticalSection(&g_cs);


    需要同步的,访问共享资源的程序代码


    LeaveCriticalSection(&g_cs);


    DeleteCriticalSection(&g_cs);             
    //
    通常在类的析构函数中

  • 相关阅读:
    webdav srs相关
    How To Configure WebDAV Access with Apache on Ubuntu 14.04
    ubuntu 编译lighttpd
    srs编译及推流测试
    Compile pciutils (lspci, setpci) in Windows x86,在 Windows x86 平台下编译 pciutils (lspci, setpci)
    mingw MSYS2 区别
    Qt之美(三):隐式共享
    Qt之美(二):元对象
    Qt之美(一):d指针/p指针详解
    C++的栈空间和堆空间
  • 原文地址:https://www.cnblogs.com/1327/p/2511500.html
Copyright © 2011-2022 走看看