C++实现简化 QtBase(6):新增Timer定时器机制

在昨天的文章《C++实现简化版Qt的QObject(5):通过IEventLoopHost扩展实现win32消息循环》里,我们对EventLoop增加了Win32消息循环的支持。
并支持post异步和延时任务。
但是,还不支持timer的多次触发的定时器。今天我们一起来增加这个功能。

接口设计

计划我们的Timer在CEventLoop中新增以下接口

		// 启动一个周期性的定时器
		CancelHandler startTimer(Handler handler, Duration interval) 
		// 停止一个周期性的定时器
		void stopTimer(CancelHandler& active) {

新增Timer机制

首先,我们希望对源代码修改尽量少的情况下增加定时器的能力。
于是,我决定在执行完task之后判断其是否是timer,如果是则再添加回task_队列中去,核心代码如下:
在这里插入图片描述

因此,task的结构体中,需要增加bool repeat字段。

取消定时器

考虑定时器的取消逻辑。一般来说设置完定时器之后会返回一个timerID之类的标识符,用于在stopTimer的时候制定具体的Timer。但是我们的任务队列task_是个std::priority_queue,只支持pop,并且遍历搜索的性能也较低,综合考虑后,我决定返回一个shared_ptr,这个指针指向一个bool标识符,标识Timer是否生效,只需要修改这个指针指向的值,就可以完成取消操作。
shared_ptr被定义为:using CancelHandle = std::shared_ptr<bool>;

增加了两个字段后,TaskEventInfo 完整的结构体定义如下:

	struct TaskEventInfo {
		TimePoint time;
		Handler handler;
		Duration interval;
		bool repeat = false;
		std::shared_ptr<bool> active;
		bool operator<(const TaskEventInfo& other) const {
			return time > other.time;
		}
	};

新增逻辑:在执行任务前,需要先验证timer是否有效:
在这里插入图片描述
那么,我们的stopTimer函数就很简单了:

		// 停止一个周期性的定时器
		void stopTimer(CancelHandle& active) {
			if (active) { *active = false; }
		}

使用示例

CWindowsEventLoopHost host;
base::CEventLoop loop(&host);

auto timerId = loop.startTimer([] {
	std::cout << "Periodic timer 500ms triggered." << std::endl;
	}, std::chrono::milliseconds(500));
	
loop.post([&]() {
	std::cout << "timer stoped!\n";
	loop.stopTimer(timerId);
	}, std::chrono::seconds(4));//延时4秒

扩展post功能

既然timer可以cancel,为什么post的任务不可以?因此我们新增一个函数,叫postCancellable,这个函数会在post的时候返回一个CancelHandle ,这样就可以取消post任务了:

//可以取消的post
CancelHandle postCancellable(Handler handler, Duration delay = Duration::zero()) {
	std::unique_lock<std::mutex> lock(mutex_);
	TaskEventInfo timedHandler{ Clock::now() + delay, std::move(handler), {}, false, std::make_shared<bool>(true) };
	tasks_.push(timedHandler);
	cond_.notify_one();
	if (host) {
		host->onPostTask();
	}
	return timedHandler.active;
}

// 停止一个周期性的定时器
void cancelPostTask(CancelHandle& active) {
	if (active) { *active = false; }
}

post函数依然返回空。之所以要新增postCancellable而非直接修改post,是因为大部分场景的post是不需要cancel的,而且如果要支持cancel需要额外new 一个shared_ptr,如果不需要cancel那就可以省下这个性能损耗了

提交代码

今天增加完这些功能,并优化以下命名,提交到git上。
https://github.com/kevinyangli/simple_qt_qobject.git

今天的代码改动如下:

 src/simple_qobject.h | 81 +++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 71 insertions(+), 10 deletions(-)

diff --git a/src/simple_qobject.h b/src/simple_qobject.h
index 35c0d03..4d2995e 100644
--- a/src/simple_qobject.h
+++ b/src/simple_qobject.h
@@ -199,7 +199,7 @@ namespace refl {
 	};
 
 	// 上面都是静态反射功能,以下是动态反射机制的支持代码:
-	
+
 	// IReflectable提供动态反射功能的支持
 	class IReflectable : public std::enable_shared_from_this<IReflectable> {
 	public:
@@ -482,10 +482,15 @@ namespace base {
 		using TimePoint = Clock::time_point;
 		using Duration = Clock::duration;
 		using Handler = std::function<void()>;
-		struct TimedHandler {
+		using CancelHandle = std::shared_ptr<bool>;
+
+		struct TaskEventInfo {
 			TimePoint time;
 			Handler handler;
-			bool operator<(const TimedHandler& other) const {
+			Duration interval;
+			bool repeat = false;
+			std::shared_ptr<bool> active;
+			bool operator<(const TaskEventInfo& other) const {
 				return time > other.time;
 			}
 		};
@@ -495,21 +500,35 @@ namespace base {
 			virtual ~IEventLoopHost() = default;
 			virtual void onPostTask() = 0;
 			virtual void onWaitForTask(std::condition_variable& cond, std::unique_lock<std::mutex>& locker) = 0;
-			virtual void onEvent(TimedHandler& event) = 0;
+			virtual void onEvent(TaskEventInfo& 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::priority_queue<TaskEventInfo> tasks_;
 		std::mutex mutex_;
 		std::condition_variable cond_;
 		std::atomic<bool> running_{ true };
 
+		static thread_local CEventLoop* s_currentThreadEventLoop;
 	public:
 		CEventLoop(IEventLoopHost* host = nullptr) {
 			this->host = host;
 			host->eventLoop = this;
+			if (s_currentThreadEventLoop == nullptr) {
+				s_currentThreadEventLoop = this;
+			}
+		}
+		~CEventLoop() {
+			if (s_currentThreadEventLoop == this) {
+				s_currentThreadEventLoop = nullptr;
+			}
+		}
+
+		CEventLoop* currentThreadEventLoop() {
+			return s_currentThreadEventLoop;
 		}
+
 		void post(Handler handler, Duration delay = Duration::zero()) {
 			std::unique_lock<std::mutex> lock(mutex_);
 			tasks_.push({ Clock::now() + delay, std::move(handler) });
@@ -519,6 +538,37 @@ namespace base {
 			}
 		}
 
+		//可以取消的post
+		CancelHandle postCancellable(Handler handler, Duration delay = Duration::zero()) {
+			std::unique_lock<std::mutex> lock(mutex_);
+			TaskEventInfo timedHandler{ Clock::now() + delay, std::move(handler), {}, false, std::make_shared<bool>(true) };
+			tasks_.push(timedHandler);
+			cond_.notify_one();
+			if (host) {
+				host->onPostTask();
+			}
+			return timedHandler.active;
+		}
+
+		// 停止一个周期性的定时器
+		void cancelPostTask(CancelHandle& active) {
+			if (active) { *active = false; }
+		}
+
+		// 启动一个周期性的定时器
+		CancelHandle startTimer(Handler handler, Duration interval) {
+			std::unique_lock<std::mutex> lock(mutex_);
+			TaskEventInfo timedHandler{ Clock::now() + interval, std::move(handler), interval, true, std::make_shared<bool>(true) };
+			tasks_.push(timedHandler);
+			cond_.notify_one();
+			return timedHandler.active;
+		}
+
+		// 停止一个周期性的定时器
+		void stopTimer(CancelHandle& active) {
+			if (active) { *active = false; }
+		}
+
 		void run() {
 			while (running_) {
 				std::unique_lock<std::mutex> lock(mutex_);
@@ -537,13 +587,24 @@ namespace base {
 					auto task = tasks_.top();
 					tasks_.pop();
 					lock.unlock();
-					if (host) {
-						host->onEvent(task);
+					bool isActive = task.active.get() ? (*task.active.get()) : true;
+					if (isActive) {
+						if (host) {
+							host->onEvent(task);
+						}
+						else {
+							task.handler();
+						}
+						lock.lock();
+
+						if (task.repeat) { // 是个timer,计算下一次触发时机并放回去
+							task.time = Clock::now() + task.interval;
+							tasks_.push(task);
+						}
 					}
 					else {
-						task.handler();
+						lock.lock();
 					}
-					lock.lock();
 				}
 
 				if (!tasks_.empty()) {
@@ -596,7 +657,7 @@ public:
 		}
 	}
 
-	void onEvent(base::CEventLoop::TimedHandler& event) override {
+	void onEvent(base::CEventLoop::TaskEventInfo& event) override {
 		event.handler();
 	}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值