如何用C++编写支持延迟回调注册与到期执行的小型库
实现思路与代码参考
疑问解答
- 注册功能逻辑:注册时不会阻塞等待,仅会把回调、绑定的参数、计算好的绝对触发时间存到内部存储中,等待后续
execute调用时统一判断执行 - 回调参数支持:可以通过lambda捕获或
std::bind实现任意数量、类型的参数绑定,不需要额外编写多套重载适配不同参数的回调
核心结构设计
- 回调存储结构:每个待执行回调需要存储触发绝对时间、绑定完参数的可执行对象两个核心字段,选择按触发时间升序排序的容器(如
std::multimap)存储可提升执行效率,不用每次遍历全量回调 - 调度器类核心接口:
register_callback:接收延迟秒数、回调函数、可变参数,计算绝对触发时间,把参数绑定到回调后存入容器execute:获取当前时间,取出所有触发时间小于等于当前时间的回调执行,执行后从容器删除(满足一次性执行要求)
伪代码参考
// 回调项定义 struct CallbackItem { TimePoint trigger_time; // 绝对触发时间 function<void()> bound_callback; // 已绑定参数的可执行对象 // 按触发时间升序排序重载 bool operator<(const CallbackItem& other) const { return trigger_time < other.trigger_time; } } class CallbackScheduler { private: sorted_container<CallbackItem> pending_callbacks; // 按触发时间排序的待执行回调容器 public: void register_callback(double delay_seconds, 回调函数, 可选参数列表) { // 计算触发的绝对时间 = 当前时间 + 延迟秒数 trigger_time = current_steady_time() + seconds(delay_seconds); // 把可选参数绑定到回调函数上,生成无参可执行对象 bound_callback = 绑定参数(回调函数, 可选参数列表); // 存入待执行容器 pending_callbacks.insert(CallbackItem{trigger_time, bound_callback}); } void execute() { now = current_steady_time(); // 找到第一个触发时间晚于当前时间的回调,前面的所有回调均已到期 end_iter = 第一个未到期回调的位置; // 遍历执行所有到期回调 for (iter = 容器头; iter != end_iter; iter++) { iter->bound_callback(); } // 批量删除所有已执行的回调 pending_callbacks.erase(容器头, end_iter); } }
完整可运行实现
#include <iostream> #include <chrono> #include <functional> #include <map> #include <thread> using TimePoint = std::chrono::steady_clock::time_point; using Seconds = std::chrono::duration<double>; class CallbackScheduler { private: // 按触发时间升序存储回调,key为触发时间,value为绑定好参数的回调 std::multimap<TimePoint, std::function<void()>> pending_callbacks; public: // 注册回调接口:延迟秒数,回调函数,任意数量可选参数 template<typename Func, typename... Args> void register_callback(double delay_seconds, Func&& func, Args&&... args) { TimePoint trigger_time = std::chrono::steady_clock::now() + Seconds(delay_seconds); // 绑定参数生成无参可执行对象 std::function<void()> bound_callback = [func = std::forward<Func>(func), ...args = std::forward<Args>(args)]() mutable { func(args...); }; pending_callbacks.insert({trigger_time, std::move(bound_callback)}); } // 执行所有到期回调 void execute() { TimePoint now = std::chrono::steady_clock::now(); // 找到第一个未到期的回调位置,之前的全部到期 auto end_iter = pending_callbacks.upper_bound(now); // 遍历执行所有到期回调 for (auto iter = pending_callbacks.begin(); iter != end_iter; ++iter) { iter->second(); } // 批量删除已执行的回调 pending_callbacks.erase(pending_callbacks.begin(), end_iter); } }; // 测试用回调函数1:带int参数 void print_num(int num) { std::cout << "回调触发:打印数字 " << num << std::endl; } // 测试用回调函数2:带字符串参数 void print_msg(const std::string& msg) { std::cout << "回调触发:打印消息 " << msg << std::endl; } int main() { CallbackScheduler scheduler; // 注册3个测试回调 scheduler.register_callback(1.0, print_num, 100); // 1秒后触发 scheduler.register_callback(2.0, print_msg, "Hello World"); // 2秒后触发 scheduler.register_callback(1.5, [](){ std::cout << "回调触发:lambda无参回调" << std::endl; }); // 1.5秒后触发 // 模拟业务循环,每500ms调用一次execute for (int i = 0; i < 5; ++i) { std::cout << "第" << i+1 << "次调用execute" << std::endl; scheduler.execute(); std::this_thread::sleep_for(std::chrono::milliseconds(500)); } return 0; }
内容的提问来源于stack exchange,提问作者Ashish
相关产品推荐
相关产品推荐

