zoukankan      html  css  js  c++  java
  • windows多线程同步--事件

    推荐参考博客:秒杀多线程第六篇 经典线程同步 事件Event

     

    事件是内核对象,多用于线程间通信,可以跨进程同步

    事件主要用到三个函数:CreateEvent,OpenEvent,SetEvent,ResetEvent                                                        本文地址

     

    CreateEvent

    函数功能:创建事件

    函数原型:

    HANDLECreateEvent(

    LPSECURITY_ATTRIBUTESlpEventAttributes,

    BOOLbManualReset,

    BOOLbInitialState,

    LPCTSTRlpName

    );

    第一个参数:表示安全控制,一般直接传入NULL。

    第二个参数:确定事件是手动置位还是自动置位,传入TRUE表示手动置位,传入FALSE表示自动置位。如果为自动置位,则对该事件调用WaitForSingleObject()后会自动调用ResetEvent()使事件变成未触发状态。打个小小比方,手动置位事件相当于教室门,教室门一旦打开(被触发),所以有人都可以进入直到老师去关上教室门(事件变成未触发)。自动置位事件就相当于医院里拍X光的房间门,门打开后只能进入一个人,这个人进去后会将门关上,其它人不能进入除非门重新被打开(事件重新被触发)。

    第三个参数:表示事件的初始状态,传入TRUR表示已触发。

    第四个参数:表示事件的名称,传入NULL表示匿名事件。

    返回值:事件的句柄

     

    OpenEvent

    函数功能:根据名称获得一个事件句柄。

    函数原型:

    HANDLEOpenEvent(

    DWORDdwDesiredAccess,

    BOOLbInheritHandle,

    LPCTSTRlpName     //名称

    );

    函数说明:

    第一个参数:表示访问权限,对事件一般传入EVENT_ALL_ACCESS。详细解释可以查看MSDN文档。

    第二个参数:表示事件句柄继承性,一般传入TRUE即可。

    第三个参数:表示名称,不同进程中的各线程可以通过名称来确保它们访问同一个事件。

    返回值:返回事件的句柄

     

    SetEvent

    函数功能:触发事件

    函数原型:BOOLSetEvent(HANDLEhEvent);

    函数说明:每次触发后,必有一个或多个处于等待状态下的线程变成可调度状态。

     

    ResetEvent

    函数功能:将事件设为末触发

    函数原型:BOOLResetEvent(HANDLEhEvent);

     

    下面从一个例子说明:假设有三个线程都需要使用打印机,我们可以使用互斥量来控制,这样就可以保证每次只有一个线程在使用打印机

    使用自动置位,那么在调用WaitForSingleObject()后会自动调用ResetEvent()使事件变为未触发状态,为了使后面的线程能够继续打印,需要在线程函数的结尾调用SetEvent来触发事件

     #include<string>
     #include<iostream>
     #include<process.h>
     #include<windows.h>
     using namespace std;
    
     //声明事件句柄
     HANDLE hev;
    
    //线程绑定的函数返回值和参数是确定的,而且一定要__stdcall
    unsigned __stdcall threadFun(void *param)
    {
        WaitForSingleObject(hev, INFINITE);//等待事件被触发
        for(int i = 0; i < 10; i++)
            cout<<*(string *)(param)<<" ";
        cout<<endl;
        SetEvent(hev);//设置事件为触发状态,使后面的线程可以打印
        return 1;
    }
    
    
    int main()
    {
        //创建一个未被触发的事件,事件是自动置位的
        hev = CreateEvent(NULL, FALSE, FALSE, NULL);
    
        SetEvent(hev);// 触发事件,使线程可以打印
    
        HANDLE hth1, hth2, hth3;
        string s1 = "first", s2 = "second", s3 = "third";
    
        //创建线程
        hth1 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s1, 0, NULL);
        hth2 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s2, 0, NULL);
        hth3 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s3, 0, NULL);
    
        //等待子线程结束
        WaitForSingleObject(hth1, INFINITE);
        WaitForSingleObject(hth2, INFINITE);
        WaitForSingleObject(hth3, INFINITE);
    
        //一定要记得关闭线程句柄
        CloseHandle(hth1);
        CloseHandle(hth2);
        CloseHandle(hth3);
    
        //千万别忘了删除事件
        CloseHandle(hev);
    }

     

    image

     

    使用手动置位,调用WaitForSingleObject()后事件就一直是触发状态,线程可以任意的打印

     #include<string>
     #include<iostream>
     #include<process.h>
     #include<windows.h>
     using namespace std;
    
     //声明事件句柄
     HANDLE hev;
    
    //线程绑定的函数返回值和参数是确定的,而且一定要__stdcall
    unsigned __stdcall threadFun(void *param)
    {
        WaitForSingleObject(hev, INFINITE);//等待事件被触发
        for(int i = 0; i < 10; i++)
            cout<<*(string *)(param)<<" ";
        cout<<endl;
        //SetEvent(hev);//设置事件为触发状态,使后面的线程可以打印
        return 1;
    }
    
    
    int main()
    {
        //创建一个未被触发的事件,事件是手动置位的
        hev = CreateEvent(NULL, TRUE, FALSE, NULL);
    
        SetEvent(hev);// 触发事件,使线程可以打印
    
        HANDLE hth1, hth2, hth3;
        string s1 = "first", s2 = "second", s3 = "third";
    
        //创建线程
        hth1 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s1, 0, NULL);
        hth2 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s2, 0, NULL);
        hth3 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s3, 0, NULL);
    
        //等待子线程结束
        WaitForSingleObject(hth1, INFINITE);
        WaitForSingleObject(hth2, INFINITE);
        WaitForSingleObject(hth3, INFINITE);
    
        //一定要记得关闭线程句柄
        CloseHandle(hth1);
        CloseHandle(hth2);
        CloseHandle(hth3);
    
        //千万别忘了删除事件
        CloseHandle(hev);
    }

     

    image

     

    再通过下面的例子来看看时间有没有所有权属性:编写一个程序,开启3个线程,这3个线程的ID分别为A、B、C,每个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示;如:ABCABC….依次递推

     #include<string>
     #include<iostream>
     #include<process.h>
     #include<windows.h>
     using namespace std;
     //声明3个事件句柄
    HANDLE  hev1, hev2, hev3;
    
    //线程绑定的函数返回值和参数是确定的,而且一定要__stdcall
    unsigned __stdcall threadFunA(void *)
    {
        for(int i = 0; i < 10; i++){
            WaitForSingleObject(hev1, INFINITE);//等待事件1
            cout<<"A";
            SetEvent(hev2);//触发事件2
        }
        return 1;
    }
    unsigned __stdcall threadFunB(void *)
    {
        for(int i = 0; i < 10; i++){
            WaitForSingleObject(hev2, INFINITE);//等待事件2
            cout<<"B";
            SetEvent(hev3);//触发事件3
        }
        return 2;
    }
    unsigned __stdcall threadFunC(void *)
    {
        for(int i = 0; i < 10; i++){
            WaitForSingleObject(hev3, INFINITE);//等待事件3
            cout<<"C";
            SetEvent(hev1);//触发事件1
        }
        return 3;
    }
    
    
    int main()
    {
        hev1 = CreateEvent(NULL, FALSE, FALSE, NULL);
        hev2 = CreateEvent(NULL, FALSE, FALSE, NULL);
        hev3 = CreateEvent(NULL, FALSE, FALSE, NULL);
        SetEvent(hev1);//触发事件1,从A开始打印
    
        HANDLE hth1, hth2, hth3;
        //创建线程
        hth1 = (HANDLE)_beginthreadex(NULL, 0, threadFunA, NULL, 0, NULL);
        hth2 = (HANDLE)_beginthreadex(NULL, 0, threadFunB, NULL, 0, NULL);
        hth3 = (HANDLE)_beginthreadex(NULL, 0, threadFunC, NULL, 0, NULL);
    
        //等待子线程结束
        WaitForSingleObject(hth1, INFINITE);
        WaitForSingleObject(hth2, INFINITE);
        WaitForSingleObject(hth3, INFINITE);
    
        //一定要记得关闭线程句柄
        CloseHandle(hth1);
        CloseHandle(hth2);
        CloseHandle(hth3);
    
        //删除事件
        CloseHandle(hev1);
        CloseHandle(hev2);
        CloseHandle(hev3);
    }

     

    image

     

    由结果可知事件不具有所有权属性,即某个线程获取事件后,一定要等待事件再次被触发。可参考本博客其他文章中临界区、互斥量、信号量的所有权属性来理解。

     

    【版权声明】转载请注明出处http://www.cnblogs.com/TenosDoIt/p/3601458.html

  • 相关阅读:
    iOS 代码规范
    Clang Format
    PullToRefreshListView手动刷新问题
    android不依赖具体activity弹出Dialog对话框,即全局性对话框
    我的手机华为荣耀7,运行android程序不输出Log
    Android从imageview中获得bitmap
    Fragment里面的ViewPager嵌套subFragment,主Fragment切换的时候subFragment出现空白Fragment的Bug
    解决某些手机RadioGroup中的RadioButton不居中的问题
    Android权限大全
    UML类图
  • 原文地址:https://www.cnblogs.com/TenosDoIt/p/3601458.html
Copyright © 2011-2022 走看看