标签: 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);
事件状态的类型
1
2
3
4
5
6
7
neosmart_event_t CreateEvent(
// true:表示手动,在 WaitEvent 后需要手动调用 ResetEvent 清除事件信号。false:表示自动,在 WaitEvent 后,系统会自动清除事件信号
bool manualReset,
// 初始状态,false 为无信号,true 为有信号
bool initialState
);
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.h
、pevents.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