Windows编程之多线程事件对象(Event Object)用法详解

news2024/10/6 4:06:18

目录

一、前言

二、基础用法

三、API详解

1.创建事件对象

2控制事件状态

3.等待事件对象:

四、实战案例

1.案例描述 

2.代码设计

 3.总设计代码

4.运行结果


一、前言

        事件对象(Event Object)是我们在大型项目中,进行多线程同步处理的时候经常用到的一种内核对象,下面我就根据它的基础本身的特点和相关的API函数,与实战案例相结合,讲述它的基础理论和用法。

二、基础用法

        在Windows编程中,事件对象(Event Objects)是一种内核对象,主要用于线程之间的同步。当多个进程需要访问共享资源时,可以通过CreateEvent创建的事件对象来控制访问顺序,避免资源冲突和数据不一致的问题。

        事件对象中经常结合进行使用的有以下四种api函数,我们掌握了这四种API函数的基本用法,可以说就掌握了事件对象(Event Object)。以下api函数分别为 CreateEvent , SetEvent,

ResetEvent  WaitForSingleObject。后面我会依次讲解各个api函数的原型以及作用。

三、API详解

1.创建事件对象

           我们可以使用 CreateEven 函数来创建一个事件对象,它是一个Windows API函数,这个函数允许你指定事件对象的安全属性、是手动重置还是自动重置、以及它的初始状态(信号态或非信号态)。下面是它的原型:

HANDLE WINAPI CreateEventW(
    _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,   //安全属性
    _In_ BOOL bManualReset,  // 复位方式:true 必须用 resetevent手动复原 false 自动还原为无信号状态
    _In_ BOOL bInitialState,  //初始状态 : true 初始状态为有信号状态: false 无信号状态
    _In_opt_ LPCWSTR lpName    //对象名称: null 无名的事件对象
    );

        可以根据原型解释,它的返回值类型为 句柄(空指针) ,函数约束类型为 WINAPI (_stdcall)

        第一个参数为  lpEventAttributes ,也就是安全属性,它是作为windows内核对象必须要有的参数类型。

        第二个参数是指复位方式,如果为TRUE,则事件对象需要显式调用ResetEvent函数来重置为无信号状态;如果为FALSE,则事件对象在单个等待线程被释放后自动重置为无信号状态。

        第三个参数为信号状态也就是指定事件对象的初始状态,如果为TRUE,则事件对象被创建时处于有信号状态;如果为FALSE,则处于无信号状态。

        第四个参数为指定事件对象的名称,通常为NULL,为无名的事件对象。

2控制事件状态

        事件对象有两种状态——发信号和不发信号。

       SetEvent:将事件对象的状态置为发信号状态,允许等待该事件的线程继续执行。

       ResetEvent:将事件对象的状态置为不发信号状态。

WINBASEAPI BOOL WINAPI SetEvent(
    _In_ HANDLE hEvent
    );

WINBASEAPI BOOL WINAPI ResetEvent(
    _In_ HANDLE hEvent
    );

        原型为上述代码,参数都是为 HANDLE (句柄),也就是 CreateEven 函数的返回值,

3.等待事件对象

      使用 WaitForSingleObject 或 WaitForMultipleObjects 函数等待一个或多个事件对象变为信号态, 线程才会继续向下执行。

        函数原型如下,参数都大致相同。

WINBASEAPI DWORD WINAPI WaitForSingleObject(
    _In_ HANDLE hHandle,
    _In_ DWORD dwMilliseconds
    );

四、实战案例

        上面我们已经讲述了事件对象的作用以及一些常用的api方法和属性,下面我将会通过一个实际有代表性的案例来继续讲解事件对象,来加深它的用法和印象。

1.案例描述 

        下面游乐园有两个售票口 A 和 售票口 B,游乐园限制最多100人进,假设这两个售票口所卖的是不同种类的票,一共有100张。那么该如何设计程序,保证售票口 A 和 售票口 B 同时所卖的票不是同一张票。

2.代码设计

        上述案例我们可以用编程的角度去分析问题和解决问题。

        售票口 A 和 售票口 B 可以分别看作两个线程,线程 A和线程 B。100张票可以当作全局变量,作为线程A,B需要访问的公共资源。代码设计为:

#include <stdio.h>
#include <windows.h>
#include <process.h>


// 共享资源 (100张票)
int iTickets = 100;

// 事件对象
HANDLE g_hEvent;

int main()
{
    // 线程A 和 线程B
    HANDLE hThreadA, hThreadB;
    hThreadA = CreateThread(NULL, 0, SellTicketA, NULL, 0, 0);
    hThreadB = CreateThread(NULL, 0, SellTicketB, NULL, 0, 0);
    CloseHandle(hThreadA); 
    CloseHandle(hThreadB);

           
    system("pause");
    return 0;
}

        那么就只需要保证线程A 和 线程 B在同一时间只能对共享资源的单一访问,这里我们就可以用到事件对象(Event Objects)。

    //手动重置 FALSE:设置无信号状态,未触发状态
    g_hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
    SetEvent(g_hEvent);
    Sleep(4000);
    CloseHandle(g_hEvent);

        具体做法为 进程A 可以通过 SetEvent函数 将事件对象的状态设置为有信号状态,进程B 则可以通 WaitForSingleObject 等函数等待该事件对象变为有信号状态,从而实现进程间的信号传递和协调,代码为:

    while (1)
    {
        WaitForSingleObject(g_hEvent, INFINITE);
        if (iTickets > 0)
        {
            Sleep(1);
            iTickets--;
            printf("A remain %d\n",iTickets);
        }
        else
        {
            break;
        }
        SetEvent(g_hEvent);
    }
    return 0;
 3.总设计代码

        以下是设计的总代码:

#include <stdio.h>
#include <windows.h>
#include <process.h>


// 共享资源 (100张票)
int iTickets = 100;

// 事件对象
HANDLE g_hEvent;

DWORD WINAPI SellTicketA(void* arg)
{
    while (1)
    {
        WaitForSingleObject(g_hEvent, INFINITE);
        if (iTickets > 0)
        {
            Sleep(1);
            iTickets--;
            printf("A remain %d\n",iTickets);
        }
        else
        {
            break;
        }
        SetEvent(g_hEvent);
    }
    return 0;
}
DWORD WINAPI SellTicketB(void* arg)
{
    while (1)
    {
        WaitForSingleObject(g_hEvent, INFINITE);
        if (iTickets > 0)
        {
            Sleep(1);
            iTickets--;
            printf("B remain %d\n", iTickets);
        }
        else
        {
            break;
        }
        SetEvent(g_hEvent);
    }
    return 0;
}


int main()
{
    // 线程A 和 线程B
    HANDLE hThreadA, hThreadB;
    hThreadA = CreateThread(NULL, 0, SellTicketA, NULL, 0, 0);
    hThreadB = CreateThread(NULL, 0, SellTicketB, NULL, 0, 0);
    CloseHandle(hThreadA); 
    CloseHandle(hThreadB);

    //手动重置 FALSE:设置无信号状态,未触发状态
    g_hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
    SetEvent(g_hEvent);
    Sleep(4000);
    CloseHandle(g_hEvent);
    system("pause");
    return 0;
}
4.运行结果

        代码运行的总结果如下。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1892517.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android TextView的属性与用法

文本控件包括TextView、EditText、AutoCompleteTextView、CheckedTextView、MultiAutoCompleteTextView、TextInputLayout等&#xff0c;其中TextView、EditText是最基本最重要的文本控件&#xff0c;是必须要掌握的文本控件。 1.TextView TextView控件用于显示文本信息&…

21.《C语言》——【位操作符】

&#x1f33b;开场语 亲爱的读者&#xff0c;大家好&#xff01;我是一名正在学习编程的高校生。在这个博客里&#xff0c;我将和大家一起探讨编程技巧、分享实用工具&#xff0c;并交流学习心得。希望通过我的博客&#xff0c;你能学到有用的知识&#xff0c;提高自己的技能&a…

从数据洞察到智慧决策:数字孪生技术在智慧水利中的应用实践,为水库管理提供强有力的数据支撑和智能分析

目录 一、引言 二、数字孪生技术的核心与原理 三、数字孪生技术在智慧水利中的应用场景 1、水库管理的挑战与需求 2、数字孪生水库管理系统的构建 四、数字孪生技术在水库管理中的具体应用案例 1、洪水预测与模拟 2、水资源配置与调度 3、大坝安全监测与评估 4、生态环…

notepad++ 中文乱码 出现小方格

从word文档直接拷贝过来的文字&#xff0c;到notepad 中文乱码 出现小方格 &#xff1a; 方法&#xff1a;设置-语言格式设置&#xff0c;字体样式更改为宋体&#xff0c;勾选“使用全局字体”&#xff0c;点保存并关闭&#xff0c;则ok

组件丰富、支持2/3D数据可视化的编辑器平台软件?

数据可视化编辑器通常用于创建交互式的图表和模型&#xff0c;可以帮助用户以更直观的方式展示数据。一些在线平台软件提供了丰富的组件&#xff0c;支持2D和3D数据可视化&#xff1a; 1、Plotly - 提供了多种语言的库&#xff0c;支持在线创建交互式图表&#xff0c;包括2D和…

实验六(1) SQL数据查询—单表查询

题目 打开ecommerce数据库&#xff0c;用SQL语句完成下列各项查询要求&#xff1a; 查询供应商表supplier中所有供应商的信息查询供应商表supplier中所有北京地区供应商的名称supname和联系电话telephone查询商品类别表category中所有商品类别的信息&#xff0c;并以“类别编号…

基于主流SpringBoot进行JavaWeb开发的学习路线

目录 一、学习路线 &#xff08;1&#xff09;第一部分&#xff08;Web前端开发的技术栈&#xff09; &#xff08;2&#xff09;第二部分&#xff08;Web后端开发&#xff09; 二、学习之后必备的技能 三、学习Web开发的基础与未来的收获 学完这一类知识目标&#xff1a;…

Markdown编辑器的基本语法

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

Stylized Modular Character Male(程式化的模块化角色-男)

一套程式化的角色模块化部件。 在这样的插槽中定制: 头 躯干 手 裤子 每个插槽都有 2 到 5 个在 URP 中工作的 PBR 材料的选项。 该项目基于官方 Unity 标准资产包中的 Ethan 默认角色。 不包含动画。 皮肤网格的 SSS 是由自发光贴图伪造的。 如果您不想要额外的发光效果,请禁…

C++:求梯形面积

梯形面积 已知上底15厘米&#xff0c;下底25厘米&#xff0c;问梯形面积值是多少&#xff1f; #include<iostream> using namespace std; int main() {//梯形的面积公式&#xff08;上底下底&#xff09; 高 2//上底变量、下底变量int s,d,h,m;s15;d25;h 2*150 * 2/s ;…

[笔记] 卷积03 - 运算的对称性 时域构建高通滤波器的失败尝试

1.卷积运算具备足够好的对称性 1.在计算卷积时&#xff0c;两个函数的位置是可以颠倒的&#xff0c;对吧&#xff1f; 在卷积运算中&#xff0c;确实可以对参与卷积的两个函数进行颠倒。这是因为卷积的定义是通过一个函数与另一个函数的翻转后的形式进行积分运算。具体来说&a…

股价持续低迷,业绩颓势不减,冀光恒难救平安银行?

文&#xff5c;新熔财经 作者&#xff5c;宏一 周一一上班&#xff0c;就听到旁边的同事感慨今年股市行情很不错&#xff0c;尤其是银行股&#xff0c;上半年累计上涨了17.02%&#xff0c;是涨幅最大的板块。 听到这里&#xff0c;我美滋滋地打开自己的账户&#xff0c;结…

21_硬件电路基础

目录 组合逻辑电路 组合逻辑电路原理 真值表 布尔代数 门电路 译码器 发光二极管LED 液晶字符显示器LCD 数据选择器 数据分配器 多路开关 时序逻辑电路 时序逻辑电路原理 时钟信号 触发器 电位触发方式触发器 边沿触发方式触发器 寄存器 移位器 计数器 总线…

Spark大数据处理:技术、应用与性能优化(全)PDF书籍推荐分享

本书从一个系统化的视角&#xff0c;秉承大道至简的主导思想&#xff0c;介绍Spark中最值得关注的内 容&#xff0c;讲解Spark部署、开发实战&#xff0c;并结合Spark的运行机制及拓展&#xff0c;帮读者开启Spark技术之旅。 Spark大数据处理&#xff1a;技术、应用与性能优化…

Python: Can‘t pop from an empty list

在 Python 中&#xff0c;如果尝试从一个空列表中弹出&#xff08;pop&#xff09;元素&#xff0c;会引发 IndexError 异常。这是因为 pop() 方法试图移除并返回列表中的最后一个元素&#xff0c;但在列表为空时无法执行此操作。那么具体情况可以跟着我看看下面文章。 1、问题…

Avalonia应用在基于Linux的国产操作deepin上运行

deepin系统介绍 deepin(原名Linux Deepin)致力于为全球用户提供美观易用&#xff0c;安全可靠的 Linux发行版。deepin项目于2008年发起&#xff0c;并在2009年发布了以 linux deepin为名称的第一个版本。2014年4月更名为 deepin&#xff0c;在中国常被称为“深度操作系统”。 …

一个项目学习Vue3---Vue计算属性

观察下面一段代码&#xff0c;学习Vue计算属性 <template><div><span>用户大于10岁的数量&#xff1a;{{ userVue.filter(user>user.age>10).length}}</span><span>用户大于10岁的数量2&#xff1a;{{ userAgeltTen}}</span><sp…

使用QStandardItemModel、QItemSelectionModel 绑定到tableView

1、使用QStandardItemModel、QItemSelectionModel 绑定到tableView&#xff0c;展示tableView的新增、插入&#xff0c;删除 、生成文档操作&#xff1b; 2、文本文件的读写 3、遍历QStandardItemModel 4、遍历 QItemSelectionModel 布局 .h #ifndef TABLEMODELVIEWEXAMPLE_H…

windows机器通过证书方式登录linux主机

1. 正常连接需要输入密码 ssh root1.1.1.1 2. 在Windows上生成SSH密钥对&#xff08;如果你还没有的话&#xff09;&#xff1a; ssh-keygen 3. scp将id_rsa.pub传输到对应的主机 4.对应机器上查看 5.从windows上免密登录

LiveNVR监控流媒体Onvif/RTSP用户手册-分屏展示:分组、轮播、四分屏、九分屏、十六分屏

LiveNVR监控流媒体Onvif/RTSP用户手册-分屏展示:分组、轮播、四分屏、九分屏、十六分屏 1、分屏展示1.1、选择通道1.2、多分屏1.3、分组1.4、轮播 2、RTSP/HLS/FLV/RTMP拉流Onvif流媒体服务 1、分屏展示 1.1、选择通道 1.2、多分屏 支持 单屏、四分屏、九分屏、十六分屏 ![请添…