基于C++实现的EventLoop与事件驱动编程

一,概念介绍
事件驱动编程(Event-Driven)是一种编码范式,常被应用在图形用户界面,应用程序,服务器开发等场景。
采用事件驱动编程的代码中,通常要有事件循环,侦听事件,以及不同事件所对应的回调函数。
事件驱动编程经常被应用在前端开发以及C++服务器开发等场景。
Event即事件,是事件驱动编程中的基本处理单元,可以理解为各种各样的信号,对于UI界面来说,鼠标点击、键盘输入、触摸屏输入都可以理解为事件。
事件循环模式(Event loop)是一种简单且高效的并发编程模式,当前业界有很多主流的C++编程框架比如libevent,libuv,Boost.Asio等都支持事件循环机制。但是考虑代码封装上的简洁,我们也可以借助C++11标准实现自己的事件循环代码。通过事件循环,程序可以支持非阻塞的异步操作,提高系统的性能。

步骤示意图:
拿Event填充Event队列:

图片

客户端只管发起请求,触发相应的事件,其他步骤交给队列去处理:

图片

EventLoop样例代码:

include

include

include

include

class EventManager {
private:
std::map > events;

public:
EventManager() {}
EventManager* eventRegist(std::string event_name, void (callback)(int)) { std::vector listeners = &events[event_name];
// if this listener is already registered, we wont add it again
if (std::find(listeners->begin(), listeners->end(), callback) != listeners->end())
{
return this;
}
listeners->push_back(callback);
return this;
}
bool emit(std::string event_name, int arg) {
std::vector listeners = events[event_name];
if (listeners.size() == 0) return false;
for (int idx = 0; idx < listeners.size(); idx += 1) {
listenersidx;
}
return true;
}
};

void callback1(int num) {
std::cout << “callback1-” << num << std::endl;
}
void callback2(int num) {
std::cout << “callback2-” << num << std::endl;
}

int main() {
EventManager* event_manager = new EventManager();
//注册回调函数
event_manager->eventRegist(“event1”, callback1);
event_manager->eventRegist(“event2”, callback2);
//执行回调函数
int eventA = event_manager->emit(“event1”, 10);
int eventB = event_manager->emit(“event2”, 20);
return 0;
}
运行结果:
callback1-10
callback2-20
根据以上代码样例,我们发现事件驱动编程通常有以下编码元素:
1.回调函数:回调函数可以是预定义的函数,也可以是匿名函数或Lambda表达式。
2.注册回调:将回调函数赋值给Event的一个std::function成员变量,再将Event添加到Event Loop对应的队列中。
3.触发Event对应的请求以后,从队列中执行事件对应的回调函数。

二,Event Loop步骤拆解
事件循环(Event loop)是一种轮询机制,这种轮询是异步的,有时候轮询和事件注册发生在不同的线程中。
事件循环特别适用于异步编程,在事件循环中,程序会不断地等待事件的发生,并根据事件的类型和优先级来执行相应的处理逻辑。

事件循环主要由以下四个部分组成:
1.事件队列(Event Queue):
用于存储待处理的事件,每个事件都包含一个回调函数和相应的函数参数。
2.事件触发器(Event Trigger):
负责监听外部事件(如用户输入、网络请求等),并将事件添加到事件队列中。
3.事件处理器(Event Handler):
从事件队列中取出对应事件,并执行事件的回调函数。
4.回调函数(Callback Function):
与特定事件相关联的函数,当对应的事件发生时才会被调用执行。回调函数只有被”注册”到事件队列中才会被调用执行。所谓的”注册”就是将回调函数赋值给Event对应的函数对象。

事件循环(Event Loop)是一个无限循环,它会不断地从事件队列中取出事件,并执行对应的回调函数。在有些模式下,事件循环会检查事件队列是否为空,如果为空则会进入休眠状态等待新的事件到来。

事件循环的基本流程如下:
step.01: 初始化事件队列。
step.02: 进入循环,等待事件的发生。
step.03: 当监听的事件被触发时,将事件添加到事件队列中。
step.04: 从事件队列中取出一个事件,并异步执行对应的回调函数。
step.05: 返回第2步,继续等待下一个事件的发生。

注意:step.01~step.05并不只发生在同一个线程中,很多时候,回调函数的调用会放在子线程中进行。

参考以上步骤,我们可以设计以下Event Loop代码:

include

include

include

include

include

class EventLoop
{
public:
using callable_t = std::function;
EventLoop() = default;
~EventLoop() noexcept
{
enqueue([this]
{
m_running = false;
});
std::cout << “step.02: other thread print.n”; m_thread.join(); } //禁用移动构造 & 拷贝构造 EventLoop(const EventLoop&) = delete; EventLoop(const EventLoop&&) = delete; EventLoop& operator= (const EventLoop&) = delete; EventLoop& operator= (const EventLoop&&) = delete; void enqueue(callable_t&& callable) noexcept { { std::lock_guard guard(m_mutex);
m_writeBuffer.emplace_back(std::move(callable));
}
m_condVar.notify_one();
}
private:
std::vector m_writeBuffer;
std::mutex m_mutex;
std::condition_variable m_condVar;
bool m_running{ true };
std::thread m_thread{ &EventLoop::threadFunc, this};
void threadFunc() noexcept
{
std::vector readBuffer;
while (m_running)
{
{
std::unique_lock lock(m_mutex);
m_condVar.wait(lock, [this]
{
return !m_writeBuffer.empty();
});
std::swap(readBuffer, m_writeBuffer);
}
for (callable_t& func : readBuffer)
{
func();
}
readBuffer.clear();
}
std::cout << “step.03: event loop end.n”;
}
};

int main()
{
EventLoop eventLoop;
eventLoop.enqueue([]
{
std::cout << “Event_01 is running.n”;
});
eventLoop.enqueue([]
{
std::cout << “Event_02 is running.n”;
});
eventLoop.enqueue([]
{
std::cout << “Event_03 is running.n”;
});
std::cout << “step.01: main thread print.n”;
}
运行结果:
step.01: main thread print.
step.02: other thread print.
Event_01 is running.
Event_02 is running.
Event_03 is running.
step.03: event loop end.

三,事件驱动代码实战
Demo1:没有添加Event Loop,主要运行Callback回调函数

include

include

include

// 定义回调函数类型
typedef std::function Callback;
// 模拟用户输入事件
void simulateUserInput(Callback callback_func) {
std::string input;
std::cout << “请输入一段文字:”;
getline(std::cin, input);
callback_func(input); // 触发回调函数
}
// 处理用户输入事件的回调函数
void handleUserInput(std::string inputStr) {
std::cout << “用户输入事件已触发!” << std::endl;
std::cout << “用户输入的是: ” << inputStr << std::endl;
return;
}

int main() {
simulateUserInput(handleUserInput);
return 0;
}
运行结果:
请输入一段文字:hello
用户输入事件已触发!
用户输入的是:hello

Demo2:

include

include

include

//定义事件类型
typedef std::function Event;
//事件队列
std::queue eventQueue;
//注册回调函数
void registerEventHandler(Event event) {
eventQueue.push(event);
}
//事件处理器
void processEvents() {
while (!eventQueue.empty()) {
Event event = eventQueue.front();
event(); //调用回调函数
eventQueue.pop();
}
}
//回调函数
void callback1() {
std::cout << “Callback 1 called” << std::endl;
}
void callback2() {
std::cout << “Callback 2 called” << std::endl;
}
int main() {
//注册回调函数到事件队列
registerEventHandler(callback1);
registerEventHandler(callback2);
//处理事件
processEvents();
return 0;
}
运行结果:
Callback 1 called
Callback 2 called

Demo3:

include

include

include

//定义Event结构体
struct Event {
std::function callback;
};
//定义事件处理器
class EventHandler {
public:
void handleEvent(Event event) {
event.callback();
}
};
//定义事件循环
class EventLoop {
public:
void addEvent(Event event) {
eventQueue.push(event);
}
void run() {
while (!eventQueue.empty()) {
Event event = eventQueue.front();
eventQueue.pop();
eventHandler.handleEvent(event);
}
}
private:
std::queue eventQueue;
EventHandler eventHandler;
};
int main() {
//创建事件循环对象
EventLoop eventLoop;
//回调函数
std::function callback1 = []() {
std::cout << “Event 1 triggered!” << std::endl; }; std::function callback2 = []() {
std::cout << “Event 2 triggered!” << std::endl;
};
//创建事件并添加到事件循环中
Event event1{ callback1 };
Event event2{ callback2 };
eventLoop.addEvent(event1);
eventLoop.addEvent(event2);
//运行事件循环
eventLoop.run();
return 0;
}
运行结果:
Event 1 triggered!
Event 2 triggered!

四,参考阅读
https://habr.com/en/articles/665730/
https://gist.github.com/kassane/f2330ef44b070f4a5fa9d59c770f68e9
https://stdcxx.apache.org/doc/stdlibug/11-3.html

声明:文中观点不代表本站立场。本文传送门:https://eyangzhen.com/415627.html

(0)
联系我们
联系我们
分享本页
返回顶部