zoukankan      html  css  js  c++  java
  • 线程同步之事件

    事件:事件Event实际上是个内核对。事件类似于前面的信号量,一个事件有两种状态:激发状态和未激发状态。也称有信号状态和无信号状态。
    事件又分两种类型:手动重置事件和自动重置事件。手动重置事件被设置为激发状态后,会唤醒所有等待的线程,而且一直保持为激发状态,直到程序重新把它设置为未激发状态。自动重置事件被设置为激发状态后,会唤醒“一个”等待中的线程,然后自动恢复为未激发状态。

    自动Event可以被抽象为四个操作:
    - 创建 CreateEvent(NULL,false,true,NULL);
    - 带触发 WaitForSingleObject(g_hEvent, INFINITE);
    - 重置激活 SetEvent(g_hEvent);
    - 销毁 CloseHandle(g_hEvent);

    手动Event可以被抽象为五个操作:
    - 创建 CreateEvent(NULL,true,true,NULL);
    - 带触发 WaitForSingleObject(g_hEvent, INFINITE);
    - 重置未激活 ResetEvent(g_hEvent);
    - 重置激活 SetEvent(g_hEvent);
    - 销毁 CloseHandle(g_hEvent);

    函数解析:

    1.
    函数功能描述:创建或打开一个命名的或无名的事件对象
    函数原型:
    HANDLE CreateEvent
    (
      LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全属性
      BOOL bManualReset, // 复位方式
      BOOL bInitialState, // 初始状态
      LPCTSTR lpName // 对象名称
    );
    参数:
    lpEventAttributes:
      一个指向SECURITY_ATTRIBUTES结构的指针,确定返回的句柄是否可被子进程继承。如果lpEventAttributes是NULL,此句柄不能被继承。如果
    lpEventAttributes是NULL,事件将获得一个默认的安全符。

    bManualReset:
      指定将事件对象创建成手动复原还是自动复原。如果是TRUE,那么必须用ResetEvent函数来手工将事件的状态复原到无信号状态。如果设置为FALSE,当事件被一个等待线程释放以后,系统将会自动将事件状态复原为无信号状态。

    bInitialState:
      指定事件对象的初始状态。如果为TRUE,初始状态为有信号状态;否则为无信号状态。
    lpName:
      指定事件的对象的名称,是一个以0结束的字符串指针。名称的字符格式限定在MAX_PATH之内。名字是对大小写敏感的。

    注意:
    1.如果lpName指定的名字,与一个存在的命名的事件对象的名称相同,函数将请求EVENT_ALL_ACCESS来访问存在的对象。这时候,由于bManualReset和bInitialState参数已经在创建事件的进程中设置,这两个参数将被忽略。如果lpEventAttributes是参数不是NULL,它将确定此句柄是否可以被继承,但是其安全描述符成员将被忽略。
    2.如果lpName为NULL,将创建一个无名的事件对象。
    3.如果lpName的和一个存在的信号、互斥、等待计时器、作业或者是文件映射对象名称相同,函数将会失败,在GetLastError函数中将返回ERROR_INVALID_HANDLE。造成这种现象的原因是这些对象共享同一个命名空间。

    注意点:

    手动与自动的区别:
    自动重置: SetEvent之后, 事件自动重置为未触发状态。
    手动重置: SetEvent之后, 需要调用ResetEvent事件才置为未触发状态。

    区别: 当一个手动重置事件被触发的时候, 正在等待该事件的所有线程都变为可调度状态; 当一个自动重置事件被触发的时候,
    只有一个正在等待该事件的线程会变为可调度状态. 系统并不会保证会调度其中的哪个线程, 剩下的线程将继续等待. 这样, 可以在在每个线程函数返回之前调用SetEvent

    换句话说:
    (1)对于手动置位事件,所有正处于等待状态下线程都变成可调度状态。
    (2)对于自动置位事件,所有正处于等待状态下线程只有一个变成可调度状态。

    2.
    函数原型:
    DWORD WaitForSingleObject
    (
      HANDLE hHandle,
      DWORD dwMilliseconds
    );
    参数解析:
    参数hHandle是一个事件的句柄,第二个参数dwMilliseconds是时间间隔(INFINITE 永久)。如果时间是有信号状态返回。

    WaitForSingleObject函数用来检测hHandle事件的信号状态。如果dwMilliseconds为有限事件,则当函数的执行时间超过dwMilliseconds就返回,
    但如果参数dwMilliseconds为INFINITE时函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去,直到WaitForSingleObject有返回直才执行后面的代码。

    注意:
    (1)如果是自动置位事件,那么每一次WaitForSingleObject后,此时状态就会变成未激发状态,就要用SetEvent()进行激活。
    (2)如果是手动置位事件,每一次WaitForSingleObject后,不会改变原有的状态。要利用ResetEvent()设置为未激活状态,再利用SetEvent()进行激活。

    3.
    SetEvent
    函数功能:触发事件
    函数原型:BOOL SetEvent(HANDLEhEvent);
    函数说明:每次触发后,必有一个或多个处于等待状态下的线程变成可调度状态。

    4.
    ResetEvent
    函数功能:将事件设为末触发
    函数原型:BOOL ResetEvent(HANDLEhEvent);

    源代码:
    // Semaphore.cpp : 定义控制台应用程序的入口点。
    //
    #include "stdafx.h"
    #include <Windows.h>
    #include <process.h>
    
    //线程数
    #define  g_nThreadNum 5
    
    //信号量
    HANDLE g_hEvent;
    
    //累加数
    int g_nCount = 0;
    
    unsigned _stdcall ThreadFunc(void * lParam)
    {
        
        for (int i = 0; i < 100000; ++i)
        {
            
            //等待事件被触发  ,触发后变成无信号
            WaitForSingleObject(g_hEvent, INFINITE);
        
            g_nCount++;
    
            //重置事件为激活
            SetEvent(g_hEvent);
        }
        return 0;
    }
    
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        //创建事件
        g_hEvent=(HANDLE)CreateEvent(NULL,false,true,NULL);
        
        //启动线程
        HANDLE pThread[g_nThreadNum];
        for (int i = 0; i < g_nThreadNum; ++i)
        {
            
            pThread[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, NULL, 0, 0);
            if (pThread[i] == 0)
            {
                continue;
                i--;
            }
        }
    
        //等待线程结束
        WaitForMultipleObjects(g_nThreadNum, pThread, TRUE, INFINITE);
    
        printf("g_nCount:%d
    ", g_nCount);
    
        //释放资源
        for (int i = 0; i < g_nThreadNum; ++i)
        {
            CloseHandle(pThread[i]);
        }
        
        CloseHandle(g_hEvent);
    
    
        getchar();
        return 0;
    }
  • 相关阅读:
    Python Module_Socket_网络编程
    Python Module_Socket_网络编程
    从 2017 OpenStack Days China 看国内云计算的发展现状
    从 2017 OpenStack Days China 看国内云计算的发展现状
    说说excel
    一种防脱裤撞库的可能性?
    黑白相片变彩色相片的一种可能性?
    为什么需求文档一定要电子化?
    一个网页布局练习
    css田字格布局
  • 原文地址:https://www.cnblogs.com/gd-luojialin/p/7694140.html
Copyright © 2011-2022 走看看