首页 > 系统相关 >Windows编程之多线程事件对象(Event Object)用法详解

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

时间:2024-07-05 12:26:17浏览次数:8  
标签:NULL hEvent Windows Object 对象 线程 事件 多线程 iTickets

目录

一、前言

二、基础用法

三、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.运行结果

        代码运行的总结果如下。

 

标签:NULL,hEvent,Windows,Object,对象,线程,事件,多线程,iTickets
From: https://blog.csdn.net/m0_62681656/article/details/140164288

相关文章

  • windows安装以及切换使用nodejs多版本
    1安装nvmnvm是一个简单的bash脚本,它是用来管理系统中多个已存的Node.js版本。可以先把系统已有的node卸载掉,也可不卸载,但是以防没必要的冲突,尽量还是卸掉。1.1下载nvm下载地址:https://github.com/coreybutler/nvm-windows/releases,下载.zip后缀的这个文件,下载后解压安装即可......
  • 18. JAVA 多线程锁介绍
    1.前言本节内容主要是对Java多线程锁进行介绍,是对锁的一个全方位的概述,为我们对后续深入学习不同的锁的使用方法奠定一个良好的基础。本节内容的知识点如下:乐观锁与悲观锁的概念,以及两种锁之间的区别,这是并发编程中经常涉及到的知识点,这是本节课程的核心知识点,是热度很高......
  • Java_多线程:线程池
    1、线程池优点:降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一......
  • Windows中配置python3.11环境安装教程
    在Windows中配置Python3.11环境,可以按照以下步骤进行:1.下载Python3.11打开Python的官方网站:https://www.python.org/导航到“Downloads”部分,并选择“Windows”。在“Windows”页面中,找到Python3.11的版本,点击下载适合你系统架构(32-bit或64-bit)的安装程序。2.安装Py......
  • 【Linux】多线程(互斥 && 同步)
    我们在上一节多线程提到没有任何保护措施的抢票是会造成数据不一致的问题的。那我们怎么办?答案就是进行加锁。目录加锁:认识锁和接口:初始化:加锁&&解锁:全局的方式:局部的方式:原理角度理解:实现角度理解:同步:加锁:认识锁和接口:初始化:这个就是我们互斥锁的类型。......
  • Windows下Docker-desktop
    1、在Windows下安装Docker-desktop下载:https://github.com/tech-shrimp/docker_installer/releases/download/latest/docker_desktop_installer_windows_x86_64.exe2、安装:start/w"""docker_desktop_installer_windows_x86_64.exe"install--installat......
  • mysql 8详细安装过程(windows 11)
        本次在windows11中安装mysql-8.4.1的压缩版。需要注意的是,其中涉及的安装配置比较多,以及需要执行的命令较多,建议大家收藏保存。一、安装环境二、下载mysql    下载地址:MySQL::DownloadMySQLCommunityServer如果没有oracle账号,点击上面下......
  • WPF Performance Suite, Microsoft Windows Performance Toolkit
    Copyfrom https://www.cnblogs.com/lindexi/p/12086719.htmlhttps://learn.microsoft.com/en-us/previous-versions/aa969767(v=vs.110) 1.Downloadurl:  https://download.microsoft.com/download/A/6/A/A6AC035D-DA3F-4F0C-ADA4-37C8E5D34E3D/setup/WinSDKPerformanceT......
  • 多线程笔记
    目录1.概念进程与线程的区别:多线程的概念 多线程优缺点​编辑创建多线程的几种方式1、继承Thread类2、实现Runnable接口3、实现Callable接口实现Runnable和Callable有什么区别:相同点:不同点:注意点:4、线程池实现多线程创建线程的方式的比较实现runnable和继承t......
  • python爬虫3-多进程多线程协程
    多进程和多线程frommultiprocessingimportProcessimportthreadingdefprocess_worker():foriinrange(200):print(f"Processworker{i}")defthread_worker():foriinrange(200):print(f"Threadworker{i}")if__......