C++实现简化 QtBase(4):增加简单实用的事件机制

前面的文章已经实现了许多QObject的功能了:
C++实现一个简单的Qt信号槽机制
C++实现简化版Qt信号槽机制(2):增加内存安全保障
C++实现简化版Qt的QObject(3):增加父子关系、属性系统

但是,Qt中还有一个关键的功能是事件系统。

为了让我们的QObject也支持事件系统,我们可以设计一套简单实用的事件机制。

设计事件循环

事件系统离不开事件循环,截止到C++ 20标准库里没有一套官方的事件循环机制,可能是因为实现一个事件循环也不是什么难事吧。

为了简化我们的事件循环机制,我们用到了一个c++11引入的独特的容器:priority_queue 。它提供了一个严格的弱序列,其中每个元素都有一个优先级。在 priority_queue 中,元素被按优先级排序,最高优先级的元素总是位于队列的前端。我们将事件循环的时间按执行时间点的远近作为优先级,可以很好地简化排序逻辑。

结合boost::asio和qt的EventLoop的使用经验,我计划这个简单实用的事件循环使用的姿势如下:

refl::CEventLoop loop;
loop.post([]() {
   
	std::cout << "Immediate task\n";
	});//马上执行

loop.post([]() {
   
	std::cout << "Delayed task\n";
	}, std::chrono::seconds(1));//延时一秒

std::thread loop_thread([&loop]() {
   
	loop.run();
	});

std::this_thread::sleep_for(std::chrono::seconds(2));
loop.stop();//停止消息循环
loop_thread.join();

实现事件循环

在确定使用方法之后,总体的接口和功能实现就比较简单了。
核心成员变量和函数功能注释如下:

class CEventLoop {
   
	std::priority_queue<TimedHandler> tasks_;//成员变量:任务队列
	std::mutex mutex_;//用于等待相关
	std::condition_variable cond_;//用于等待相关
	std::atomic<bool> running_{
    true };//标识是否正在运行
	
	// "post" 函数用于提交一个待执行的任务到事件循环中。
	// 参数 "handler" 是一个函数对象,代表需要异步执行的任务。
	// 参数 "delay" 表示任务延迟执行的时间,默认是立即执行(Duration::zero())。
	void post(Handler handler, Duration delay = Duration::zero()) {
   
	    // 对互斥量上锁,保证线程安全。
	    std::unique_lock<std::mutex> lock(mutex_);
	    // 将任务和它应该被执行的时间点(现在 + 延迟)一起存入优先级队列中。
	    tasks_.push({
   Clock::now() + delay, std::move(handler)});
	    // 通知一个等待中的线程(如果有的话),有新的任务已经被提交。
	    cond_.notify_one();
	}
	
	// "run" 函数启动事件循环,循环内部不断地执行任务。
	void run() {
   
	    // 只要 "running_" 标志为 true,事件循环就会继续运行。
	    while (running_) {
   
	        // 对互斥量上锁,保证线程安全。
	        std::unique_lock<std::mutex> lock(mutex_);
	        // 如果当前没有任务可执行,就等待直到有新任务被提交或者事件循环被停止。
	        if (tasks_.empty()) {
   
	            cond_.wait(lock, [this] {
    return !tasks_.empty() || !running_; });
	        }
	
	        // 当有任务可以执行时(优先级队列中最早的任务时间 <= 当前时间),执行它们。
	        while (!tasks_.empty() && tasks_.top().first <= Clock::now()) {
   
	            // 从队列中取出任务。
	            auto task = std::move(tasks_.top());
	            // 将任务从队列中移除。
	            tasks_.pop();
	            // 释放互斥量锁,以便其他线程可以提交任务或者修改任务队列。
	            lock.unlock();
	            // 执行任务。
	            task.second();
	            // 任务执行完毕后,再次上锁互斥量。
	            lock.lock();
	        }
	
	        // 如果队列中还有任务,等待直到队列中最早的任务到达执行时间。
	        if (!tasks_.empty()) {
   
	            // 等待直到最早任务的执行时间,或者条件变量被通知。
	            cond_.wait_until(lock, tasks_.top().first);
	        }
	    }
	}
};

虽然几十行代码实现的时间循环非常精简,但是也不能少了扩展能力,通过扩展可以实现更复杂的主消息循环等复杂场景。于是我们引入一个回调类IEventLoopHost:

		class IEventLoopHost {
   
		public:
			virtual void onWaitForTask(std::condition_variable& cond, std::unique_lock<std::mutex>& locker) = 0;
			virtual void onEvent(TimedHandler& event) = 0;
			virtual void onWaitForRun(std::condition_variable& cond, std::unique_lock<std::mutex>& locker, const TimePoint& timePoint) = 0;
		};

在特定时机可以通过回调类替换掉默认实现,从而实现完整的功能扩展。

事件循环的完整代码

后续放到github上迭代(https://github.com/kevinyangli/simple_qt_qobject.git)

class CEventLoop {
   
public:
	using Clock = std::chrono::steady_clock;
	using TimePoint = Clock::time_point;
	using Duration = Clock::duration;
	using Handler = std::function<void()>;
	struct TimedHandler {
   
		TimePoint time;
		Handler handler;
		bool operator<(const TimedHandler& other) const {
   
			return time > other.time;
		}
	};
	class IEventLoopHost {
   
	public:
		virtual void onWaitForTask(std::condition_variable& cond, std::unique_lock<std::mutex>& locker) = 0;
		virtual void onEvent(TimedHandler& event) = 0;
		virtual void onWaitForRun(std::condition_variable& cond, std::unique_lock<std::mutex>& locker, const TimePoint& timePoint) = 0;
	};
private:
	IEventLoopHost* host = nullptr;
	std::priority_queue<TimedHandler> tasks_;
	std::mutex mutex_;
	std::condition_variable cond_;
	std::atomic<bool> running_{
    true };

public:
	void setHost(IEventLoopHost* host) {
   
		this->host = host;
	}
	void post(Handler handler, Duration delay = Duration::zero()) {
   
		std::unique_lock<std::mutex> lock(mutex_);
		tasks_.push({
    Clock::now() + delay, std::move(handler) });
		cond_.notify_one();
	}

	void run() {
   
		while (running_) {
   
			std::unique_lock<std::mutex> lock(mutex_);
			if (tasks_.empty()) {
   
				if (host) {
   
					host->onWaitForTask(cond_, lock);
				}
				else {
   
					cond_.wait(lock, [this] {
    return !tasks_.empty() || !running_; });
				}
			}

			while (!tasks_.empty() && tasks_.top().time <= Clock::now()) {
   
				auto task = tasks_.top();
				tasks_.pop();
				lock.unlock();
				if (host) {
   
					host->onEvent(task);
				}
				else {
   
					task.handler();
				}

				lock.lock();
			}

			if (!tasks_.empty()) {
   
				if (host) {
   
					host->onWaitForRun(cond_, lock, tasks_.top().time);
				}
				else {
   
					cond_.wait_until(lock, tasks_.top().time);
				}
			}
		}
	}

	void stop() {
   
		running_ = false;
		cond_.notify_all();
	}

};

带上完整的测试代码,以及之前的功能实现:

#include <iostream>
#include <tuple>
#include <stdexcept>
#include <assert.h>
#include <string_view>
#include <optional>
#include <utility> // For std::forward
#include <unordered_map>
#include <functional>
#include <memory>
#include <any>
#include <type_traits> // For std::is_invocable
#include <map>

#include <chrono>
#include <thread>
#include <vector>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <atomic>

namespace refl {
   

	// 这个宏用于创建字段信息
#define REFLECTABLE_PROPERTIES(TypeName, ...)  using CURRENT_TYPE_NAME = TypeName; \
    static constexpr auto properties_() {
      return std::make_tuple(__VA_ARGS__); }
#define REFLECTABLE_MENBER_FUNCS(TypeName, ...) using CURRENT_TYPE_NAME = TypeName; \
    static constexpr auto member_funcs() {
      return std::make_tuple(__VA_ARGS__); }

// 这个宏用于创建属性信息,并自动将字段名转换为字符串
#define REFLEC_PROPERTY(Name) refl::Property<decltype(&CURRENT_TYPE_NAME::Name), &CURRENT_TYPE_NAME::Name>(#Name)
#define REFLEC_FUNCTION(Func) refl::Function<decltype(&CURRENT_TYPE_NAME::Func), &CURRENT_TYPE_NAME::Func>(#Func)

// 定义一个属性结构体,存储字段名称和值的指针
	template <typename T, T Value>
	struct Property {
   
		const char* name;
		constexpr Property(const char* name) : name(name) {
   }
		constexpr T get_value() con
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值