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

目录

一、前言

二、基础用法

三、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.mfbz.cn/a/770199.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

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

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

如何对低代码平台进行分类?

现在市面上的低代码平台就像雨后春笋一样冒出来&#xff0c;而且源源不绝&#xff0c;但总结下来&#xff0c;大致的也就以下三类。 一、 aPaaS多引擎类&#xff08;有很多成熟引擎、做好东西要一起用&#xff09; 这类产品包括&#xff1a;织信Informat&#xff08;国内&…

照明物联网:基于网关的智能照明云监控系统解决方案

智能照明系统就是利用物联网技术&#xff0c;将同一空间的照明、空调、新风、排风等系统共同接入物联网平台&#xff0c;实现了“设备互联、数据互通”的智慧物联能力。照明数据、环境监测数据通过网关上传云端&#xff0c;在云端进行统计分析并将结果通过各种终端共享&#xf…

MySQL—常用的数据类型

数据类型 整型 1.创建一个含有无符号/有符号整型的字段的表 CREATE TABLE L1(id tinyint unsigned #无符号 ) CREATE TABLE L2(id tinyint #默认为有符号 ) 数值型(bit) 2.数值型(bit)的使用 小数 3.数值型(小数)的基本使用 字符串 4.字符串的基本使用 #演示字符串类型…

REGX52.H报错

keil cannot open source input file "REGX52.H": No such file or directory 选择下面这个目录 Keil\C51\INC\Atmel

AI绘画Stable Diffusion 新手入门教程:万字长文解析Lora模型的使用,快速上手Lora模型!

大家好&#xff0c;我是设计师阿威 今天给大家讲解一下AI绘画Stable Diffusion 中的一个重要模型—Lora模型&#xff0c;如果还有小伙伴没有SD安装包的&#xff0c;可以看我往期入门教程2024最新超强AI绘画Stable Diffusion整合包安装教程&#xff0c;零基础入门必备&#xff…

【软件测试】Selenium自动化测试框架 | 相关介绍 | Selenium + Java环境搭建 | 常用API的使用

文章目录 自动化测试一、selenium1.相关介绍1.Selenium IDE2.Webdriverwebdriver的工作原理&#xff1a; 3.selenium Grid 2.Selenium Java环境搭建3.常用API的使用1.定位元素2.操作测试对象3.添加等待4.打印信息5.浏览器的操作6.键盘事件7.鼠标事件8.定位一组元素9.多层框架定…

手把手家教你进行ChatGPT私有化部署

背景 随着AI技术的不断成熟&#xff0c;加上ChatGPT如火如荼的发布新版本迭代更新&#xff0c;人工智能的热度也升温到史无前例的高度。 我们有理由相信&#xff0c;现在身边还不愿主动去接触这项技术&#xff0c;深入了解的小伙伴&#xff0c;在不久的将来&#xff0c;一定会…

网络攻防——kali操作系统基本使用

1.阅读前的声明 本文章中生成的木马带有一定的攻击性&#xff0c;使用时请遵守网络安全相关的法律法规&#xff08;恶意攻击操作系统属于违法行为&#xff09;。 2.环境安装 生成木马主要需要如下工具&#xff1a;kali操作系统&#xff0c;VMware15&#xff08;搭建kali操作…

用Python制作动态钟表:实时显示时间的动画

文章目录 引言准备工作前置条件 代码实现与解析导入必要的库初始化Pygame绘制钟表函数主循环 完整代码 引言 动态钟表是一种直观且实用的UI元素&#xff0c;能够实时显示当前时间。在这篇博客中&#xff0c;我们将使用Python创建一个动态钟表&#xff0c;通过利用Pygame库来实…

无线物联网题集

测试一 未来信息产业的发展在由信息网络向 全面感知和 智能应用两个方向拓展、延伸和突破。 各国均把 物联网作为未来信息化战略的重要内容,融合各种信息技术,突破互联网的限制,将物体接入信息网络。 计算机的出现,开始了第四次工业革命,开始了人机物的高度融合&#xff08;&…

LVS负载均衡群集部署之——DR模式的介绍及搭建步骤

一、LVS-DR集群介绍1.1 LVS-DR 工作原理1.2 数据包流向分析1.3 LVS-DR 模式的特点1.4 LVS-DR中的ARP问题1.4.1 问题一1.4.2 问题二二、构建LVS-DR集群2.1 构建LVS-DR集群的步骤&#xff08;理论&#xff09;1.配置负载调度器&#xff08;192.168.80.30&#xff09;&#xff08;…

护眼指南之适合学生写作业的台灯:看看学生护眼台灯哪个品牌好

随着人们健康意识的提高&#xff0c;越来越多的人开始关注眼睛的健康问题&#xff0c;照明技术的进步也为缓解眼疲劳提供了可能&#xff0c;现在的照明产品可以通过调整光线亮度、色温、频闪等参数&#xff0c;使光线更加柔和、均匀&#xff0c;减少眼睛的不适感。人们都希望通…

重生奇迹MU 最动听的声音 最精彩的游戏

在重生奇迹MU的世界里&#xff0c;每个玩家都是重生奇迹的见证者&#xff0c;同时也是重生奇迹的创造者。每个玩家都有属于自己的冒险故事&#xff0c;每时每刻都会有新的喜悦降临。这款神奇的游戏让人沉浸于冒险的精彩中&#xff0c;实在引人入胜。 “叮”的一声让你倍感喜悦…

<Linux> 多线程

文章目录 线程线程互斥锁死锁 线程同步生产者消费者模型POSIX信号量基于环形队列的生产消费模型 线程池 线程 线程是进程内部可以独立运行的最小单位 进程是资源分配的基本单位&#xff0c;线程是调度器调度的基本单位 线程在进程的地址空间内运行 进程内的大部分资源线程是…

LeetCode-刷题记录-滑动窗口合集(本篇blog会持续更新哦~)

一、滑动窗口概述 滑动窗口&#xff08;Sliding Window&#xff09;是一种用于解决数组&#xff08;或字符串&#xff09;中子数组&#xff08;或子串&#xff09;问题的有效算法。 Sliding Window核心思想&#xff1a; 滑动窗口技术的基本思想是维护一个窗口&#xff08;一般…

RPC远程过程调用--Thrift

RPC远程过程调用–Thrift 简介 Thrift是一个由Facebook开发的轻量级、跨语言的远程服务调用框架&#xff0c;后进入Apache开源项目。支持通过自身接口定义语言IDL定义RPC接口和数据类型&#xff0c;然后通过编译器生成不同语言代码&#xff0c;用于构建抽象易用、可互操作的R…

JAVA+SSM+VUE《教学视频点播系统》

1管理员登录 管理员登录&#xff0c;通过填写用户名、密码、角色等信息&#xff0c;输入完成后选择登录即可进入视频点播系统&#xff0c;如图1所示。 图1管理员登录界面图 2管理员功能实现 2.1 修改密码 管理员对修改密码进行填写原密码、新密码、确认密码并进行删除、修改…

【Python机器学习】算法链与管道——在网格搜索中使用管道

在网格搜索中使用管道的工作原理与使用任何其他估计器都相同。 我们定义一个需要搜索的参数网络&#xff0c;并利用管道和参数网格构建一个GridSearchCV。不过在指定参数网格时存在一处细微的变化。我们需要为每个参数指定它在管道中所属的步骤。我们要调节的两个参数C和gamma…

监控与安全服务

kali 系统 nmap扫描 网段的扫描 使用脚本扫描 使用john破解密码 哈希算法是一种单向加密的算法&#xff0c;也就是将原始数据生成一串“乱码”只能通过原始数据&#xff0c;生成这串“乱码”&#xff0c;但是不能通过“乱码”回推出原始数据相同的原始数据&#xff0c;生成的乱…