首页 > 系统相关 >Linux 实现 Windows 的 Event 事件机制

Linux 实现 Windows 的 Event 事件机制

时间:2023-03-15 18:23:07浏览次数:62  
标签:Windows pid pevents Event int Linux tid neosmart event

前言

Linux 中没有 Windows 系统中的 CreateEvent()WaitEvent()SetEvent()ResetEvent() 等函数,本文将介绍如何使用 pevents 替代 Linux 缺失的函数。

pevents 介绍

pevents 的简介

pevents 是一个跨平台的轻量级 C++ 库,旨在为 POSIX 系统提供 WIN32 事件的实现。pevents 提供了 Windows 平台手动和自动重置事件的大部分功能,最显著的是支持同时等待多个事件(WaitForMultipleObjects),而且支持 Windows、FreeBSD、Linux、macOS、iOS、Android 等平台。

pevents 的 API

API 函数

pevents 的 API 是根据 Windows 的 CreateEvent()WaitEvent() 和 WaitForMultipleObjects() 函数编写的,熟悉 WIN32 事件的开发人员应该可以将代码库切换到 pevents API。虚假唤醒是 Linux 下系统编程的正常部分,也是来自 Windows 世界的开发人员的常见陷阱,pevents 可以保证不存在虚假唤醒和等待返回的数据的正确性,其提供了如下的 API:

1
2
3
4
5
6
7
8
9
10
int SetEvent(neosmart_event_t event);
int ResetEvent(neosmart_event_t event);
int PulseEvent(neosmart_event_t event);

int DestroyEvent(neosmart_event_t event);
neosmart_event_t CreateEvent(bool manualReset, bool initialState);

int WaitForEvent(neosmart_event_t event, uint64_t milliseconds);
int WaitForMultipleEvents(neosmart_event_t *events, int count, bool waitAll, uint64_t milliseconds);
int WaitForMultipleEvents(neosmart_event_t *events, int count, bool waitAll, uint64_t milliseconds, int &index);
 

事件状态的类型

  • CreateEvent() 函数
1
2
3
4
5
6
7
neosmart_event_t CreateEvent(
    // true:表示手动,在 WaitEvent 后需要手动调用 ResetEvent 清除事件信号。false:表示自动,在 WaitEvent 后,系统会自动清除事件信号
    bool manualReset, 

    // 初始状态,false 为无信号,true 为有信号
    bool initialState
);
 
  • WaitForEvent() 函数
1
2
3
4
5
6
int WaitForEvent(
    // 句柄对象
    neosmart_event_t event,
    // 等待的时间(毫秒)
    uint64_t milliseconds
);
 
  • 事件状态的类型
    • WAIT_TIMEOUT:等待超时
    • WAIT_OBJECT_0:句柄对象处于有信号状态
    • WAIT_FAILED:出现错误,可通过 GetLastError() 函数得到错误码
    • WAIT_ABANDONED:说明句柄代表的对象是个互斥对象,并且正在被其它线程占用

注意

在 Linux 平台,pevents 的事件状态只支持使用 WAIT_TIMEOUT,且有信号的时候 WaitEvent() 函数的返回值是 0,而在 Windows 平台则支持上述四种事件状态

pevents 的项目结构

  • 核心代码在 src/ 目录
  • 单元测试代码(通过 Meson 构建)在 test/ 目录
  • 在 examples/ 目录中可以找到演示 pevents 用法的跨平台应用示例程序

pevents 的编译构建

pevents 使用的构建工具是 Meson,目前这仅用于支持 pevents 核心代码及其单元测试的自动化构建 / 测试。值得一提的是,开发人员不需要担心构建工具的差异性,pevents 是特意基于 C/C++ 标准编写的,避免了复杂的配置或依赖于平台的构建指令的需要。

pevents 的编译参数

通过编译参数 -DWFMO 与 -DPULSE,可以在编译时让 pevents 启用不同的功能:

  • WFMO:启用 WFMO 功能,如果需要使用 WaitForMultipleEvents() 函数,建议仅使用 WFMO 进行编译,因为它会为所有事件对象增加开销(较小)。
  • PULSE:启用 PulseEvent 功能,PulseEvent() 在 Windows 平台从根本上被破坏了,一般不应该被使用,当你调用它时,它几乎永远不会做你认为你正在做的事情。pevents 包含这个函数只是为了让现有的(有缺陷的)代码从 WIN32 移植到 Unix/Linux 平台更容易,并且这个函数默认没有编译到 pevents 中。

Meson 指定编译参数

在 Meson 中,可以通过 meson_options.txt 配置文件指定编译参数,让 pevents 启用不同的功能

1
2
3
4
option('wfmo', type: 'boolean', value: true,
	description: 'Enable WFMO events')
option('pulse', type: 'boolean', value: false,
	description: 'Enable PulseEvent() function')
 

CMake 指定编译参数

在 CMake 中,可以通过 CMakeLists.txt 配置文件指定编译参数,让 pevents 启用不同的功能

1
set(CMAKE_CXX_FLAGS "-std=c++11 -lpthread -DWFMO")
 

pevents 运行示例代码

提示

值得一提的是,pevents 的核心 C++ 源文件是 pevents.hpevents.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 拉取代码
$ git clone [email protected]:clay-world/pevents.git

# 进入源码目录
$ cd pevents

# 生成构建的输出目录
$ meson build

# 进入构建的输出目录
$ cd build

# 编译代码
$ ninja

# 运行示例程序
$ ./sample
 

pevents 的实战案例

编译说明

下面给出的案例使用了 pthread,由于 pthread 不是 Linux 系统默认的库,因此链接时需要使用静态库 libpthread.a。简而言之,在使用 pthread_create() 创建线程,以及调用 pthread_atfork() 函数建立 fork 处理程序时,需要通过 -lpthread 参数链接该库,同时还需要在 C++ 源文件里添加头文件 pthread.h

提示

为了可以正常编译使用了 pthread 的项目代码,不同构建工具的使用说明如下:

若使用 G++ 编译 C++ 项目,则编译命令的示例如下:

1
2
# 编译代码
$ g++ main.cpp -o main -lpthread
 

若使用 CMake 构建 C++ 项目,则 CMakeLists.txt 配置文件的示例内容如下:

1
2
3
set(CMAKE_CXX_FLAGS "-std=c++11 -lpthread -DWFMO")

add_executable(main main.cpp)
 

实战案例一

CreateEvent(true, true) - 手动清除事件信号,初始状态为有信号,点击下载 基于 CMake 构建的完整案例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include "pevents.h"

using namespace std;
using namespace neosmart;

neosmart_event_t g_hEvent = NULL;

void printIds(const char *s) {
    pid_t pid = getpid();
    pthread_t tid = pthread_self();
    printf("%s pid %u tid %u (0x%x)\n", s, (unsigned int) pid, (unsigned int) tid, (unsigned int) tid);
}

void *procFunc1(void *args) {
    printIds("thread-1");
    if (WaitForEvent(g_hEvent, 1) == 0) {
        cout << "thread-1 is working..." << endl;
    }
    return ((void *) 0);
}

void *procFunc2(void *args) {
    printIds("thread-2");
    if (WaitForEvent(g_hEvent, 1) == 0) {
        cout << "thread-2 is working..." << endl;
    }
    return ((void *) 0);
}

int main() {
    // 手动清除事件信号,初始状态为有信号
    g_hEvent = CreateEvent(true, true);

    pthread_t ntid1;
    pthread_create(&ntid1, NULL, procFunc1, NULL);

    sleep(1);

    pthread_t ntid2;
    pthread_create(&ntid2, NULL, procFunc2, NULL);

    sleep(5);
}
 

程序运行的结果如下:

1
2
3
4
thread-1 pid 62705 tid 2336241408 (0x8b403700)
thread-1 is working...
thread-2 pid 62705 tid 2327848704 (0x8ac02700)
thread-2 is working...
 

提示

可以看到线程 1 和线程 2 都完整执行了,这是因为创建的事件是需手动 Reset 才会变为无信号的,所以执行完线程 1 后事件仍处于有信号的状态,所以线程 2 的逻辑才会被继续执行。

实战案例二

CreateEvent(false, true) - 自动清除事件信号,且初始状态为有信号,点击下载 基于 CMake 构建的完整案例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include "pevents.h"

using namespace std;
using namespace neosmart;

neosmart_event_t g_hEvent = NULL;

void printIds(const char *s) {
    pid_t pid = getpid();
    pthread_t tid = pthread_self();
    printf("%s pid %u tid %u (0x%x)\n", s, (unsigned int) pid, (unsigned int) tid, (unsigned int) tid);
}

void *procFunc1(void *args) {
    printIds("thread-1");
    if (WaitForEvent(g_hEvent, 1) == 0) {
        cout << "thread-1 is working..." << endl;
    }
    return ((void *) 0);
}

void *procFunc2(void *args) {
    printIds("thread-2");
    if (WaitForEvent(g_hEvent, 1) == 0) {
        cout << "thread-2 is working..." << endl;
    }
    return ((void *) 0);
}

int main() {
    // 自动清除事件信号,初始状态为有信号
    g_hEvent = CreateEvent(false, true);

    pthread_t ntid1;
    pthread_create(&ntid1, NULL, procFunc1, NULL);

    sleep(1);

    pthread_t ntid2;
    pthread_create(&ntid2, NULL, procFunc2, NULL);

    sleep(5);
}
 

程序运行的结果如下:

1
2
3
thread-1 pid 59685 tid 2245932800 (0x85de3700)
thread-1 is working...
thread-2 pid 59685 tid 2237540096 (0x855e2700)
 

提示

可以看到只有线程 1 完整执行了,这是由于事件在执行完线程 1 后被系统自动重置为无信号,所以线程 2 中的逻辑没有被执行。

实战案例三

CreateEvent(true, false) - 手动清除事件信号,初始状态为无信号,包括 SetEvent() 与 ResetEvent() 的使用,点击下载 基于 CMake 构建的完整案例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include "pevents.h"

using namespace std;
using namespace neosmart;

neosmart_event_t g_hEvent = NULL;

void printIds(const char *s) {
    pid_t pid = getpid();
    pthread_t tid = pthread_self();
    printf("%s pid %u tid %u (0x%x)\n", s, (unsigned int) pid, (unsigned int) tid, (unsigned int) tid);
}

void *procFunc1(void *args) {
    printIds("thread-1");
    if (WaitForEvent(g_hEvent, 1) == 0) {
        cout << "thread-1 is working..." << endl;
    }
    // 重置事件为无信号
    ResetEvent(g_hEvent);
    return ((void *) 0);
}

void *procFunc2(void *args) {
    printIds("thread-2");
    if (WaitForEvent(g_hEvent, 1) == 0) {
        cout << "thread-2 is working..." << endl;
    }
    return ((void *) 0);
}

void func1() {
    // 手动清除事件信号,初始状态为有信号
    g_hEvent = CreateEvent(true, true);

    pthread_t ntid1;
    pthread_create(&ntid1, NULL, procFunc1, NULL);

    sleep(1);

    pthread_t ntid2;
    pthread_create(&ntid2, NULL, procFunc2, NULL);

    sleep(5);
}

int main() {
    // 手动清除事件信号,初始状态为无信号
    g_hEvent = CreateEvent(true, false);

    // 设置事件为有信号
    SetEvent(g_hEvent);

    pthread_t ntid1;
    pthread_create(&ntid1, NULL, procFunc1, NULL);

    sleep(1);

    pthread_t ntid2;
    pthread_create(&ntid2, NULL, procFunc2, NULL);

    sleep(5);

    return 0;
}
 

程序运行的结果如下:

1
2
3
thread-1 pid 70368 tid 2745513728 (0xa3a53700)
thread-1 is working...
thread-2 pid 70368 tid 2737121024 (0xa3252700)
 

提示

可以看到只有线程 1 完整执行了,这是因为线程 1 在执行之前事件是有信号的,执行完成后事件被手动重置为无信号,所以线程 2 中的逻辑没有被执行。

参考资料

标签:Windows,pid,pevents,Event,int,Linux,tid,neosmart,event
From: https://www.cnblogs.com/lidabo/p/17219518.html

相关文章

  • 跨平台实现Event事件(Linux+win)
    1、第一种方法(win和linux通用)//头文件.h#ifndef_HIK_EVENT_H_  #define_HIK_EVENT_H_   #ifdef_MSC_VER #include<Windows.h> #definehik_event_handleH......
  • 修改linux shell 提示符
    远程登录虚拟机,linuxshell提示符为-bash-4.1$要把这个提示符改掉 1、新建~/.bash_profile文件-bash-4.1$vi~/.bash_profile-bash-4.1$cat ~/.bash_profilecat:......
  • 浅谈Linux下的shell--BASH
    shell的概念与作用我们已经学习并知道了操作系统实际上就是一款软件,一款用来管理计算机软硬件资源,为用户提供良好的执行环境的软件。假如该软件能被用户随意操作,就会有可能......
  • linux系统如何查看是否是线程死锁,多线程中如何使用gdb精确定位死锁问题
    在多线程开发过程中很多人应该都会遇到死锁问题,死锁问题也是面试过程中经常被问到的问题,这里介绍在c++中如何使用gdb+python脚本调试死锁问题,以及如何在程序运行过程中......
  • windows常用快捷键
    windows常用快捷键复制ctrl+c粘贴ctrl+v全选ctrl+a剪切ctrl+r撤销ctrl+z保存ctrl+s关闭窗口ctrl+F4永久删除shift+delete运行win+r我的电脑win+e任务......
  • Linux开启root用户远程登录
    Linux开启root用户远程登录开启root账户,给root用户设置密码sudopasswdroot输入两遍密码修改配置文件打开SSH配置文件。vim/etc/ssh/sshd_config修改如下参......
  • mysql安装详细文档(windows)
    安装MySQLMySQL是目前最为流行的开放源码的数据库,是完全网络化的跨平台的关系型数据库系统,它是由瑞典MySQLAB公司开发,目前属于Oracle公司。任何人都能从Internet下载MySQL......
  • linux自定义man搜索路径
    很多时候,在linux我们源码编译库代码时候会自定义安装路径,这使得man查询的时候无法找到库文档,默认的man搜索路径可以使用下面命令查看:$man-w/usr/local/share/man:/usr/......
  • Linux基础
    常用命令cd  :  切换文件夹  cd/home 绝对路径,以根目录开头  cd admin 相对路径,  cd..返回上一层目录  cd~回到自己家目录  cd-回看......
  • Linux下文档的压缩与打包
    Linux下最常见的压缩文件通常都是.tar.gz格式的,除此之外还有.tar、.gz、.bz2、.zip下面介绍Linux下最常见的后缀名所对应的压缩工具:.gz:表示由gzip压缩工具压缩的文件。......