mirror of
https://github.com/wqking/eventpp.git
synced 2024-12-25 23:30:49 +08:00
commit
ba48543b79
96
doc/cn/introduction.md
Normal file
96
doc/cn/introduction.md
Normal file
@ -0,0 +1,96 @@
|
||||
# eventpp 库介绍
|
||||
|
||||
eventpp 有三个核心的类:CallbackList, EventDispatcher 和 EventQueue ,三者各有其特定的用途。
|
||||
|
||||
## CallbackList 类
|
||||
|
||||
CallbackList 是 eventpp 中最基础的类,EventDispatcher 和 EventQueue 类的实现都建立在 CallbackList 之上。
|
||||
|
||||
CallbackList 会维护一个回调函数列表。当一个 CallbackList 被调用时,该 CallbackList 会逐个调用其中的回调函数。CallbackList 可以类比为 Qt 中的信号槽系统,以及某些 Windows API 中的回调函数指针(比如 `ReadFileEx` 中的 IpCompletionRoutine )
|
||||
|
||||
这里的“回调函数”可以是任何能够回调的目标——函数、函数指针、成员函数指针、lambda表达式、函数对象等。
|
||||
|
||||
eventpp 中的 CallbackList 相当于是 Qt 这类事件系统中的 “信号”,但 eventpp 中并没有特定的相当于 “槽”(回调函数)的东西 —— 在 eventpp 中,任何可调用的目标都可以是槽(回调函数)
|
||||
|
||||
当应用场景中的事件种类很少时,直接用 CallbackList 即可满足需求:为每个事件创建一个 CallbackList,每个 CallbackList 都可以有不同的原型( prototype ),例如:
|
||||
|
||||
```c++
|
||||
eventpp::CallbackList<void()> onStart;
|
||||
eventpp::CallbackList<void(MyStopReason)> onStop;
|
||||
```
|
||||
|
||||
当程序中有成百上千个事件时( GUI 和游戏程序中经常会有这么多的事件),上面这种写法就难以应付了——为每个事件都单独创建 CallbackList 肯定不是什么好选择。针对存在大量事件的场景,eventpp 设计了 EventDispatcher
|
||||
|
||||
## EventDispatcher 类
|
||||
|
||||
EventDispatcher 类似于 `std::map<EventType, CallbackList>` ,能根据 EventType 寻找对应 CallbackList。
|
||||
|
||||
EventDispatcher 维护一个 `<EventType, CallbackList>` 映射表。在进行事件分发时, EventDispatcher 会根据事件类型(EventType)查找并调用对应的回调列表(CallbackList) 。该调用过程是同步的,监听器会在 `EventDispatcher::dispatch` 被调用时触发。
|
||||
|
||||
EventDispatcher 适用于事件种类繁多或无法预先确定事件数量的场景。每个事件都可以根据事件类型进行分类,例如:
|
||||
|
||||
```c++
|
||||
enum class MyEventType
|
||||
{
|
||||
redraw,
|
||||
mouseDown,
|
||||
mouseUp,
|
||||
// 后面也许还有 2000 个事件
|
||||
};
|
||||
|
||||
struct MyEvent
|
||||
{
|
||||
MyEventType type;
|
||||
// 此处定义所有事件可能会需要的数据
|
||||
};
|
||||
|
||||
struct MyEventPolicies
|
||||
{
|
||||
static MyEventType getEvent(const MyEvent &e) {
|
||||
return e.type;
|
||||
}
|
||||
};
|
||||
|
||||
eventpp::EventDispatcher<MyEventType, void(const MyEvent &), MyEventPolicies> dispatcher;
|
||||
dispatcher.dispatch(MyEvent { MyEventType::redraw });
|
||||
```
|
||||
|
||||
(提示:若想了解上面代码中的 `MyEventPolicies` ,请阅读https://github.com/wqking/eventpp/blob/master/doc/policies.md 文档。本篇文档中可以暂且只关注分发器: `eventpp::EventDispatcher<MyEventType, void(const MyEvent &)> dispatcher`)
|
||||
|
||||
EventDispatcher 的缺点是,分发器中所有事件都必须有相同的回调原型(例如示例代码中的 `void(const MyEvnet &)`)。通常可以通过下面的方案来规避该缺点:将 Event 基类作为回调函数的参数,然后所有的事件都通过继承 Event 来传递他们自己的数据。在示例代码中,`MyEvent` 就是事件的基类,回调函数接收 `const MyEvent &` 。
|
||||
|
||||
## EventQueue 类
|
||||
|
||||
EventQueue 包含了 EventDispatcher 的所有特性,并在此基础上添加了事件队列。注意:EventQueue 并不是 EventDispatcher 的子类,因此不要进行类型转换。
|
||||
|
||||
EventQueue 是异步的。事件会在调用 `EventQueue::enqueue` 时被保存在队列中,并在 `EventQueue::process` 被调用时被处理。
|
||||
|
||||
EventQueue 相当于 Qt 中的事件系统(QEvent)或 Windows API 中的消息处理( message processing )。
|
||||
|
||||
```c++
|
||||
eventpp::EventQueue<int, void (const std::string &, const bool)> queue;
|
||||
|
||||
// 将事件加入队列。第一个参数永远是事件类型
|
||||
// 监听器不会在进入队列期间被触发
|
||||
queue.enqueue(3, "Hello", true);
|
||||
queue.enqueue(5, "World", false);
|
||||
|
||||
// 处理事件队列,运行队列中的所有事件
|
||||
queue.process();
|
||||
```
|
||||
|
||||
## 线程安全
|
||||
|
||||
所有类都是线程安全的,可以在多个线程中同时调用所有的公共函数。如果出现问题,请到 eventpp 的项目仓库报告 bug。
|
||||
本库能够确保每个单独函数调用的整体性,如 `EventDispatcher::appendListener`, `CallbackList::remove` ,但无法确保多个线程中的操作执行顺序。例如,若在某个线程分发一个事件的同一时刻,另一个线程移除了一个监听器(listener),则被移除的监听器仍有可能在其被移除之后触发。
|
||||
|
||||
## 异常安全
|
||||
|
||||
所有的类都不会抛出异常。但库代码所依赖的代码可能会在下列情况发生时抛出异常:
|
||||
|
||||
1. 内存耗尽,无法分配新的内存空间;
|
||||
2. 监听器(回调函数)在复制、移动、对比或调用时抛出异常
|
||||
|
||||
几乎所有操作都有很强的异常安全性,这意味着下层数据抛出异常时能够保持其原始值。唯一的例外是 `EventQueue::process`:在抛出异常时,剩余的事件就不再会被分发了,而且队列会被清空。
|
||||
|
||||
|
175
doc/cn/tutorial_callbacklist.md
Normal file
175
doc/cn/tutorial_callbacklist.md
Normal file
@ -0,0 +1,175 @@
|
||||
# CallbackList 使用教程
|
||||
|
||||
注意:如果想尝试运行教程代码,建议使用 `tests/unittest` 目录下的代码。本文中的示例代码可能已经过期而无法编译。
|
||||
|
||||
### CallbackList 教程 1, 基础
|
||||
|
||||
**代码**
|
||||
|
||||
```c++
|
||||
// 命名空间是 eventpp
|
||||
// 首个参数是监听器的原型
|
||||
eventpp::CallbackList<void ()> callbackList;
|
||||
|
||||
// 添加一个回调函数,此处即 [](){} 。回调函数并非一定要是 lambda 表达式。
|
||||
// 函数、std::function 或其他任何满足监听器原型要求的函数对象都可以作为监听器
|
||||
callbackList.append([](){
|
||||
std::cout << "Got callback 1." << std::endl;
|
||||
});
|
||||
callbackList.append([](){
|
||||
std::cout << "Got callback 2." << std::endl;
|
||||
});
|
||||
|
||||
// 启动回调列表
|
||||
callbackList();
|
||||
```
|
||||
|
||||
**输出**
|
||||
|
||||
> Got callback 1.
|
||||
> Got callback 2.
|
||||
|
||||
**解读**
|
||||
|
||||
首先,定义一个回调列表( callback list )
|
||||
|
||||
```c++
|
||||
eventpp::CallbackList<void ()> callbackList;
|
||||
```
|
||||
|
||||
CallbackList 需要至少一个模板参数,作为回调函数的“原型”( prototype )。
|
||||
“原型”指 C++ 函数类型,例如 `void (int)`, `void (const std::string &, const MyClass &, int, bool)`
|
||||
|
||||
然后,添加一个回调函数
|
||||
|
||||
```c++
|
||||
callbackList.append([]() {
|
||||
std::cout << "Got callback 1." << std::endl;
|
||||
});
|
||||
```
|
||||
|
||||
`append` 函数接收一个回调函数作为参数。
|
||||
回调函数可以使任何回调目标——函数、函数指针、指向成员函数的指针、lambda 表达式、函数对象等。该回调函数必须可以使用 `callbackList` 中声明的原型调用。
|
||||
|
||||
接下来启动回调列表
|
||||
|
||||
```c++
|
||||
callbackList();
|
||||
```
|
||||
|
||||
在回调列表启动执行的过程中,所有回调函数都会按照被加入列表时的顺序执行。
|
||||
|
||||
### CallbackList 教程 2, 带参数的回调函数
|
||||
|
||||
**代码**
|
||||
|
||||
```c++
|
||||
// 下面这个 CallbackList 的回调函数原型有两个参数
|
||||
eventpp::CallbackList<void (const std::string &, const bool)> callbackList;
|
||||
|
||||
callbackList.append([](const std::string & s, const bool b) {
|
||||
std::cout<<std::boolalpha<<"Got callback 1, s is " << s << " b is " << b << std::endl;
|
||||
});
|
||||
|
||||
// 回调函数原型不需要和回调函数列表完全一致。只要参数类型兼容即可
|
||||
callbackList.append([](std::string s, int b) {
|
||||
std::cout<<std::boolalpha<<"Got callback 2, s is " << s << " b is " << b << std::endl;
|
||||
});
|
||||
|
||||
// 启动回调列表
|
||||
callbackList("Hello world", true);
|
||||
```
|
||||
|
||||
**输出**
|
||||
|
||||
> Got callback 1, s is Hello world b is true
|
||||
> Got callback 2, s is Hello world b is 1
|
||||
|
||||
**解读**
|
||||
|
||||
本例中,回调函数列表的回调函数原型接收两个参数: `const std::string &` 和 `const bool`。
|
||||
回调函数的原型并不需要和回调完全一致,只要两个函数中的参数能够兼容即可。正如上面例子中的第二个回调函数,其参数为 `[](std::string s, int b)`,其原型与回调列表中的并不相同。
|
||||
|
||||
### CallbackList 教程 3, 移除
|
||||
|
||||
**代码**
|
||||
|
||||
```c++
|
||||
using CL = eventpp::CallbackList<void ()>;
|
||||
CL callbackList;
|
||||
|
||||
CL::Handle handle2;
|
||||
|
||||
// 加一些回调函数
|
||||
callbackList.append([]() {
|
||||
std::cout << "Got callback 1." << std::endl;
|
||||
});
|
||||
handle2 = callbackList.append([]() {
|
||||
std::cout << "Got callback 2." << std::endl;
|
||||
});
|
||||
callbackList.append([]() {
|
||||
std::cout << "Got callback 3." << std::endl;
|
||||
});
|
||||
|
||||
callbackList.remove(handler2);
|
||||
|
||||
// 启动回调列表。“Got callback 2.” 并不会被触发
|
||||
callbackList();
|
||||
```
|
||||
|
||||
**输出**
|
||||
|
||||
> Got callback 1.
|
||||
> Got callback 3.
|
||||
|
||||
### CallbackList 教程 4, for each
|
||||
|
||||
**代码**
|
||||
|
||||
```c++
|
||||
using CL = eventpp::CallbackList<void ()>;
|
||||
CL callbackList;
|
||||
|
||||
// 添加回调函数
|
||||
callbackList.append([]() {
|
||||
std::cout << "Got callback 1." << std::endl;
|
||||
});
|
||||
callbackList.append([]() {
|
||||
std::cout << "Got callback 2." << std::endl;
|
||||
});
|
||||
callbackList.append([]() {
|
||||
std::cout << "Got callback 3." << std::endl;
|
||||
});
|
||||
|
||||
// 下面调用 forEach 移除第二个回调函数
|
||||
// forEach 回调函数的原型是
|
||||
// void(const CallbackList::Handle & handle, const CallbackList::Callback & callback)
|
||||
int index = 0;
|
||||
callbackList.forEach([&callbackList, &index](const CL::Handle & handle, const CL::Callback & callback) {
|
||||
std::cout << "forEach(Handle, Callback), invoked " << index << std::endl;
|
||||
if(index == 1) {
|
||||
callbackList.remove(handle);
|
||||
std::cout << "forEach(Handle, Callback), removed second callback" << std::endl;
|
||||
}
|
||||
++index;
|
||||
});
|
||||
|
||||
// forEach 回调函数原型也可以是 void(const CallbackList::Callback & callback)
|
||||
callbackList.forEach([&callbackList, &index](const CL::Callback & callback) {
|
||||
std::cout << "forEach(Callback), invoked" << std::endl;
|
||||
});
|
||||
|
||||
// 启动回调列表。“Got callback 2.” 并不会被触发
|
||||
callbackList();
|
||||
```
|
||||
|
||||
**输出**
|
||||
|
||||
> forEach(Handle, Callback), invoked 0
|
||||
> forEach(Handle, Callback), invoked 1
|
||||
> forEach(Handle, Callback), removed second callback
|
||||
> forEach(Handle, Callback), invoked 2
|
||||
> forEach(Callback), invoked
|
||||
> forEach(Callback), invoked
|
||||
> Got callback 1.
|
||||
> Got callback 3.
|
162
doc/cn/tutorial_eventdispatcher.md
Normal file
162
doc/cn/tutorial_eventdispatcher.md
Normal file
@ -0,0 +1,162 @@
|
||||
# EventDispatcher 使用教程
|
||||
|
||||
注意:如果想尝试运行教程代码,建议使用 `tests/unittest` 目录下的代码。本文中的示例代码可能已经过期而无法编译。
|
||||
|
||||
### 教程 1 基本用法
|
||||
|
||||
**代码**
|
||||
|
||||
```C++
|
||||
// 命名空间为 eventpp
|
||||
// 第一个模板参数 int 是事件类型。事件类型可以是其他数据类型的,如 std::string,int 等
|
||||
// 第二个参数是监听器的原型
|
||||
eventpp::EventDispatcher<int, void ()> dispatcher;
|
||||
|
||||
// 添加一个监听器。这里的 3 和 5 是传给 dispatcher 的,用于标记自身的事件类型
|
||||
// []() {} 是监听器。
|
||||
// 监听器并不必须是 lambda,可以使任何满足原型要求的可调用对象,如函数、std::function等
|
||||
dispatcher.appendListener(3, []() {
|
||||
std::cout << "Got event 3." << std::endl;
|
||||
});
|
||||
dispatcher.appendListener(5, []() {
|
||||
std::cout << "Got event 5." << std::endl;
|
||||
});
|
||||
dispatcher.appendListener(5, []() {
|
||||
std::cout << "Got another event 5." << std::endl;
|
||||
});
|
||||
|
||||
// 分发事件。第一个参数是事件类型。
|
||||
dispatcher.dispatch(3);
|
||||
dispatcher.dispatch(5);
|
||||
```
|
||||
|
||||
**输出**
|
||||
|
||||
> Got event 3.
|
||||
> Got event 5.
|
||||
> Got another event 5.
|
||||
|
||||
**解读**
|
||||
|
||||
首先定义一个分发器
|
||||
|
||||
```c++
|
||||
eventpp::EventDispatcher<int, void ()> dispatcher;
|
||||
```
|
||||
|
||||
EventDispatcher 类接收两个模板参数。第一个是*事件类型*,此处是 `int` 。第二个是监听器的*原型*。
|
||||
*事件类型* 必须能够用作 `std::map` 的 key。也就是说该类型必须支持 `operator <`。
|
||||
*原型* 是 C++ 函数类型,例如 `void (int)`, `void (const std::string &, const MyClass &, int, bool)`
|
||||
|
||||
然后添加一个监听器
|
||||
|
||||
```c++
|
||||
dispatcher.appendListener(3, []() {
|
||||
std::cout << "Got event 3." << std::endl;
|
||||
});
|
||||
```
|
||||
|
||||
`appendListener` 函数接收两个参数。第一个是 *事件类型* 的 *事件* (译注:此处的“事件类型”指的是用于区分事件的数据类型,此处为 int 。“事件”则是具体的时间值,此处为整数 3 ),此处为 `int` 类型。第二个参数是*回调函数*。
|
||||
回调函数可以是任何能够回调的目标——函数、函数指针、成员函数指针、lambda表达式、函数对象等。其必须能够被 `dispatcher` 中声明的 *原型* 调用。
|
||||
在上面这段代码的下面,我们还为 事件5 添加了两个监听器。
|
||||
|
||||
接下来,使用下面的代码分发事件
|
||||
|
||||
```c++
|
||||
dispatcher.dispatch(3);
|
||||
dispatcher.dispatch(5);
|
||||
```
|
||||
|
||||
这里分发了两个事件,分别是事件 3 和 5 。
|
||||
在事件分发的过程中,所有对应事件的监听器都会按照它们被添加进 EventDispatcher 的顺序逐个执行。
|
||||
|
||||
### 教程 2 —— 带参数的监听器
|
||||
|
||||
**代码**
|
||||
|
||||
```c++
|
||||
// 定义有两个参数的监听器原型
|
||||
eventpp::EventDispatcher<int, void (const std::string &, const bool)> dispatcher;
|
||||
|
||||
dispatcher.appendListener(3, [](const std::string & s, const bool b) {
|
||||
std::cout << std::boolalpha << "Got event 3, s is " << s << " b is " << b << std::endl;
|
||||
});
|
||||
// 监听器的原型不需要和 dispatcher 完全一致,只要参数类型能够兼容即可
|
||||
dispatcher.appendListener(5, [](std::string s, int b) {
|
||||
std::cout << std::boolalpha << "Got event 5, s is " << s << " b is " << b << std::endl;
|
||||
});
|
||||
dispatcher.appendListener(5, [](const std::string & s, const bool b) {
|
||||
std::cout << std::boolalpha << "Got another event 5, s is " << s << " b is " << b << std::endl;
|
||||
});
|
||||
|
||||
// 分发事件。第一个参数是事件类型
|
||||
dispatcher.dispatch(3, "Hello", true);
|
||||
dispatcher.dispatch(5, "World", false);
|
||||
```
|
||||
|
||||
**输出**
|
||||
|
||||
> Got event 3, s is Hello b is true
|
||||
> Got event 5, s is World b is 0
|
||||
> Got another event 5, s is World b is false
|
||||
|
||||
**解读**
|
||||
|
||||
此处的 dispatcher 回调函数原型接收两个参数:`const std::string &` 和 `const bool`。
|
||||
监听器原型不需要和 dispatcher 完全一致,只要参数类型能够兼容即可。例如第二个监听器,`[](std::string s, int b)`,其原型和 dispatcher 并不相同
|
||||
|
||||
### 教程 3 —— 自定义事件结构
|
||||
|
||||
**代码**
|
||||
|
||||
```c++
|
||||
// 定义一个能够保存所有参数的 Event
|
||||
struct MyEvent {
|
||||
int type;
|
||||
std::string message;
|
||||
int param;
|
||||
};
|
||||
|
||||
// 定义一个能让 dispatcher 知道如何展开事件类型的 policy
|
||||
struct MyEventPolicies
|
||||
{
|
||||
static int getEvent(const MyEvent & e, bool /*b*/) {
|
||||
return e.type
|
||||
}
|
||||
};
|
||||
|
||||
// 将刚刚定义的 MyEventPolicies 用作 EventDispatcher 的第三个模板参数
|
||||
// 注意:第一个模板参数是事件类型的类型 int ,并非 MyEvent
|
||||
eventpp::EventDispatcher<
|
||||
int,
|
||||
void (const MyEvent &, bool),
|
||||
MyEventPolicies
|
||||
> dispatcher;
|
||||
|
||||
// 添加一个监听器。注意,第一个参数是事件类型 int,并非 MyEvent
|
||||
dispatcher.appendListener(3, [](const MyEvent & e, bool b) {
|
||||
std::cout
|
||||
<< std::boolalpha
|
||||
<< "Got event 3" << std::endl
|
||||
<< "Event::type is " << e.type << std::endl
|
||||
<< "Event::message is " << e.message << std::endl
|
||||
<< "Event::param is " << e.param << std::endl
|
||||
<< "b is " << b << std::endl
|
||||
;
|
||||
});
|
||||
|
||||
// 启动事件。第一个参数是 Event
|
||||
dispatcher.dispatch(MyEvent { 3, "Hello world", 38 }, true);
|
||||
```
|
||||
|
||||
**输出**
|
||||
|
||||
> Got event 3
|
||||
> Event::type is 3
|
||||
> Event::message is Hello world
|
||||
> Event::param is 38
|
||||
> b is true
|
||||
|
||||
**解读**
|
||||
|
||||
通常的方法是将 Event 类定义为基类,所有其他的事件都从 Event 派生,实际的事件类型则是 Event 的成员(就像 Qt 中的 QEvent ),通过 policy 来为 EventDispatcher 定义如何从 Event 类中获取真正需要的数据。
|
116
doc/cn/tutorial_eventqueue.md
Normal file
116
doc/cn/tutorial_eventqueue.md
Normal file
@ -0,0 +1,116 @@
|
||||
# EventQueue 使用教程
|
||||
|
||||
注意:如果想尝试运行教程代码,建议使用 `tests/unittest` 目录下的代码。本文中的示例代码可能已经过期而无法编译。
|
||||
|
||||
### 教程 1 基本用法
|
||||
|
||||
**代码**
|
||||
|
||||
```C++
|
||||
eventpp::EventQueue<int, void (const std::string &, std::unique_ptr<int> &)> queue;
|
||||
|
||||
queue.appendListener(3, [](const std::string & s, std::unique_ptr<int> & n) {
|
||||
std::cout << "Got event 3, s is " << s << " n is " << *n << std::endl;
|
||||
});
|
||||
|
||||
// 监听器原型不需要和 dispatcher 完全一致,参数类型兼容即可
|
||||
queue.appendListener(5, [](std::string s, const std::unique_ptr<int> & n) {
|
||||
std::cout << "Got event 5, s is " << s << " n is " << *n << std::endl;
|
||||
});
|
||||
queue.appendListener(5, [](const std::string & s, std::unique_ptr<int> & n) {
|
||||
std::cout << "Got another event 5, s is " << s << " n is " << *n << std::endl;
|
||||
});
|
||||
|
||||
// 将事件加入队列,首个参数是事件类型。监听器在入队列期间不会被触发
|
||||
queue.enqueue(3, "Hello", std::unique_ptr<int>(new int(38)));
|
||||
queue.enqueue(5, "World", std::unique_ptr<int>(new int(58)));
|
||||
|
||||
// 处理事件队列,分发队列中的所有事件
|
||||
queue.process();
|
||||
```
|
||||
|
||||
**输出**
|
||||
|
||||
> Got event 3, s is Hello n is 38
|
||||
> Got event 5, s is World n is 58
|
||||
> Got another event 5, s is World n is 58
|
||||
|
||||
**解读**
|
||||
`EventDispatcher<>::dispatch()` 触发监听器的动作是同步的。但异步事件队列在某些场景下能发挥更大的作用(例如 Windows 消息队列、游戏中的消息队列等)。EventQueue 就是用于满足该类需求的事件队列。
|
||||
`EventQueue<>::enqueue()` 将事件加入队列,其参数和 `dispatch` 的参数完全相同。
|
||||
`EventQueue<>::process()` 用于分发队列中的事件。不调用 process ,事件就不会被分发。
|
||||
事件队列的典型用例:在 GUI 应用中,每个组件都调用 `EventQueue<>::enqueue()` 来发布事件,然后主事件循环调用 `EventQueue<>()::process()` 来 dispatch 所有队列中的事件。
|
||||
`EventQueue` 支持将不可拷贝对象作为事件参数,例如上面例子中的 unique_ptr
|
||||
|
||||
### 教程 2 —— 多线程
|
||||
|
||||
**代码**
|
||||
|
||||
```c++
|
||||
using EQ = eventpp::EventQueue<int, void (int)>;
|
||||
EQ queue;
|
||||
|
||||
constexpr int stopEvent = 1;
|
||||
constexpr int otherEvent = 2;
|
||||
|
||||
// 启动一个新线程来处理事件队列。所有监听器都会在该线程中启动运行
|
||||
std::thread thread([stopEvent, otherEvent, &queue]() {
|
||||
volatile bool shouldStop = false;
|
||||
queue.appendListener(stopEvent, [&shouldStop](int) {
|
||||
shouldStop = true;
|
||||
});
|
||||
queue.appendListener(otherEvent, [](const int index) {
|
||||
std::cout << "Got event, index is " << index << std::endl;
|
||||
});
|
||||
|
||||
while(! shouldStop) {
|
||||
queue.wait();
|
||||
|
||||
queue.process();
|
||||
}
|
||||
});
|
||||
|
||||
// 将一个主线程的事件加入队列。在休眠 10 ms 时,该事件应该已经被另一个线程处理了
|
||||
queue.enqueue(otherEvent, 1);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
std::cout << "Should have triggered event with index = 1" << std::endl;
|
||||
|
||||
queue.enqueue(otherEvent, 2);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
std::cout << "Should have triggered event with index = 2" << std::endl;
|
||||
|
||||
{
|
||||
// EventQueue::DisableQueueNotify 是一个 RAII 类,能避免唤醒其他的等待线程。
|
||||
// 所以该代码块内不会触发任何事件。
|
||||
// 当需要一次性添加很多事件,希望在事件都添加完成后才唤醒等待线程时,
|
||||
// 就可以使用 DisableQueueNotify
|
||||
EQ::DisableQueueNotify disableNotify(&queue);
|
||||
|
||||
queue.enqueue(otherEvent, 10);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
std::cout << "Should NOT trigger event with index = 10" << std::endl;
|
||||
|
||||
queue.enqueue(otherEvent, 11);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
std::cout << "Should NOT trigger event with index = 11" << std::endl;
|
||||
}
|
||||
// DisableQueueNotify 对象在此处销毁,恢复唤醒其他的等待线程。因此事件都会在此处触发
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
std::cout << "Should have triggered events with index = 10 and 11" << std::endl;
|
||||
|
||||
queue.enqueue(stopEvent, 1);
|
||||
thread.join();
|
||||
```
|
||||
|
||||
**输出**
|
||||
|
||||
> Got event, index is 1
|
||||
> Should have triggered event with index = 1
|
||||
> Got event, index is 2
|
||||
> Should have triggered event with index = 2
|
||||
> Should NOT trigger event with index = 10
|
||||
> Should NOT trigger event with index = 11
|
||||
> Got event, index is 10
|
||||
> Got event, index is 11
|
||||
> Should have triggered events with index = 10 and 11
|
||||
|
Loading…
x
Reference in New Issue
Block a user