C++笔试题之实现一个定时器

一.定时器(timer)的需求

1.执行定时任务的时,主线程不阻塞,所以timer必须至少持有一个线程用于执行定时任务
2.考虑到timer线程资源的合理利用,一个timer需要能够管理多个定时任务,所以timer要支持增删任务,通过容器储存任务
3.当timer空闲时(即没有任务或执行任务的时刻未到),timer中的线程不应该空转来占用资源,可通过条件变量实现
4.支持重复任务和非重复任务

二.定时器(timer)的实现

#include <algorithm>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <map>
#include <mutex>
#include <thread>
#include <iostream>
#include <iomanip>
#include <sstream>namespace CC
{
using TaskFunc = std::function<void()>;struct Task
{uint64_t id;uint64_t period;bool repeated;TaskFunc func;bool removed;Task(uint64_t id, uint64_t period, bool repeated, TaskFunc func): id(id), period(period), repeated(repeated), func(func), removed(false){}
};class Timer
{
public:Timer() : m_stop(false){m_worker = std::thread(&Timer::run, this);}~Timer(){m_stop.store(true);m_condition.notify_all();m_worker.join();}uint64_t add(uint64_t period_ms, bool repeated, TaskFunc func){uint64_t when = now() + period_ms;Task task(m_cur_id, period_ms, repeated, func);{std::lock_guard<std::mutex> lock(m_tasks_mutex);m_tasks.insert({when, task});}m_condition.notify_all();return m_cur_id++;}// Timer::remove并没有真正的将定时任务删除,仅仅是将removed标志位设置为true,删除操作实际是在Timer::run中进行的。// 为什么要这么做?如果在这里如果由Timer::remove来执行m_tasks.erase(it),那么有可能删除的是Timer::run里正在执行的那个任务,这是明显不对的。// 所以才采用将removed标志位设置为true的这种做法。bool remove(uint64_t id){bool flag = false;std::lock_guard<std::mutex> lock(m_tasks_mutex);std::multimap<uint64_t, Task>::iterator it =std::find_if(m_tasks.begin(), m_tasks.end(),[id](const std::pair<uint64_t, Task> &item) -> bool { return item.second.id == id; });if (it != m_tasks.end()){it->second.removed = true;flag = true;}return flag;}private:std::thread m_worker;std::atomic<bool> m_stop;std::multimap<uint64_t, Task> m_tasks;std::mutex m_tasks_mutex;std::condition_variable m_condition;uint64_t m_cur_id;// m_condition.wait之后继续向下执行,此时如果m_stop是true,那么表明timer要被停止了,那线程也要结束,所以一个break跳出最开始的while (true)循环,让线程执行结束。// 如果m_stop是false,那表明现有可能有定时任务需要执行了。取出第一个任务m_tasks.begin(),也是按时间排序最靠前的任务。用任务的时刻和当前时刻对比:// 如果“时辰已到”,那就执行。执行的之后需要注意的是,要将锁释放lock.unlock(),因为继续持有没有任何意义,反而会阻塞住对m_tasks的一些操作。// 如果“时辰未到”,那就执行m_condition.wait_for,让当前线程休眠,直到std::chrono::milliseconds(task_time - cur_time)这段时间过去或者被唤醒。void run(){while (true){std::unique_lock<std::mutex> lock(m_tasks_mutex);m_condition.wait(lock, [this]() -> bool { return !m_tasks.empty() || m_stop; });if (m_stop){break;}uint64_t cur_time = now();std::multimap<uint64_t, Task>::iterator it = m_tasks.begin();uint64_t task_time = it->first;if (cur_time >= task_time){Task &cur_task = it->second;if (!cur_task.removed){lock.unlock();cur_task.func();lock.lock();if (cur_task.repeated && !cur_task.removed){uint64_t when = cur_time + cur_task.period;Task new_task(cur_task.id, cur_task.period, cur_task.repeated, cur_task.func);m_tasks.insert({when, new_task});}}m_tasks.erase(it);}else{m_condition.wait_for(lock, std::chrono::milliseconds(task_time - cur_time));}}}uint64_t now() //ms{auto now = std::chrono::system_clock::now();auto duration = now.time_since_epoch();return std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();}
};} // namespace CC// 格式化时间,精确到毫秒.
std::string getTimeString()
{auto now = std::chrono::system_clock::now();auto duration = now.time_since_epoch();auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();std::time_t time = std::chrono::system_clock::to_time_t(now);std::tm *tm = std::localtime(&time);std::stringstream ss;ss << std::put_time(tm, "%Y-%m-%d %H:%M:%S") << "." << std::setw(3) << std::setfill('0') << millis % 1000;return ss.str();
}// 待执行的任务
void theTask(int id)
{std::cout << getTimeString() << " id = " << id << std::endl;
}int main()
{CC::Timer *timer = new CC::Timer();timer->add(3000, false, std::bind(theTask, 1));uint64_t id = timer->add(2000, true, std::bind(theTask, 2));timer->add(1000, true, std::bind(theTask, 3));std::this_thread::sleep_for(std::chrono::seconds(3));timer->remove(id);std::this_thread::sleep_for(std::chrono::seconds(1));delete timer;std::this_thread::sleep_for(std::chrono::seconds(1));return 0;
}

参考链接:https://zhuanlan.zhihu.com/p/668916073

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/464609.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

0.STM32F1移植到F0的各种经验总结

1.结构体的声明需放在函数的最前面 源代码&#xff1a; /*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //开启USART1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructu…

在Microsoft Outlook日历中添加多个时区

在Microsoft Outlook日历中添加多个时区 1.单击Outlook中的文件选项卡&#xff0c;单击选项 2.左侧菜单中选择日历 3.向下滚动到时区部分&#xff0c;并标记当前时区&#xff0c;比如China 4.选中“显示第二个时区”框 5.选择第二个时区并给它一个标签&#xff0c;比如Germa…

为啥学习数据结构和算法

基础知识就像是一座大楼的地基&#xff0c;它决定了我们的技术高度。而要想快速做出点事情&#xff0c;前提条件一定是基础能力过硬&#xff0c;“内功”要到位。 想要通关大厂面试&#xff0c;千万别让数据结构和算法拖了后腿 我们学任何知识都是为了“用”的&#xff0c;是为…

爬虫学习4

from threading import Thread#创建任务 def func(name):for i in range(100):print(name,i)if __name__ __main__:#创建线程t1 Thread(targetfunc,args("1"))t2 Thread(targetfunc, args("2"))t1.start()t2.start()print("我是诛仙剑")from …

【Maven】——基础入门,插件安装、配置和简单使用,Maven如何设置国内源

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 引入&#xff1a; 一&#xff1a;Maven插件的安装 1&#xff1a;环境准备 2&#xff1a;创建项目 二…

Vue中使用echarts生成地图步骤详解

1.创建容器元素 <div class"map" id"map" style"width:1000px;height:1000px;"></div> 2.Vue项目引入world.js(我这里的演示是世界地图&#xff0c;不同地图对应js文件不一样) world.js文件包含&#xff1a; 地理坐标数据&#xff…

docker安装低版本的jenkins-2.346.3,在线安装对应版本插件失败的解决方法

提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、网上最多的默认解决方法1、jenkins界面配置清华源2、替换default.json文件 二、解决低版本Jenkins在线安装插件问题1.手动下载插件并导入2.低版本jenkins在…

算法专题:栈

目录 1. 删除字符串中的所有相邻重复项 1.1 算法原理 1.2 算法代码 2. 844. 比较含退格的字符串 2.1 算法原理 2.2 算法原理 3. 基本计算器 II 3.1 算法原理 3.2 算法代码 4. 字符串解码 4.1 算法原理 4.2 算法代码 5. 验证栈序列 5.1 算法原理 5.2 算法代码 1.…

ZDH权限-扩展支持数据权限

目录 项目源码 预览地址 安装包下载地址 ZDH权限模块 ZDH权限扩展更细粒度方案 第一种方案&#xff1a; 第二种方案&#xff1a; ZDH权限扩展支持数据权限-新增属性 总结 感谢支持 项目源码 zdh_web: GitHub - zhaoyachao/zdh_web: 大数据采集,抽取平台 预览地址 后…

交换机的基本配置

交换机的基本配置 实验题目实验目的实验任务实验设备实验环境实验步骤VLAN 的简单配置跨交换机 vlan 的配置主机配置信息表解释&#xff1a; vlan 间路由 实验题目 交换机的基本配置。 实验目的 1) 理解交换机的原理和应用场景&#xff1b; 2) 交换机的基本指令系统&#xf…

借助 Aspose.Words,使用 C# 从 Word 文档中删除页面

如果您正在寻找一种快速删除 Word 文档中不相关、过时或空白页的方法&#xff0c;那么您来对地方了。在这篇博文中&#xff0c;我们将学习如何使用 C# 从 Word 文档中删除页面。我们将逐步引导您完成该过程&#xff0c;提供清晰的示例&#xff0c;以帮助您以编程方式高效地从 W…

华为 HarmonyOS NEXT 原生应用开发: 动画的基础使用(属性、显示、专场)动画

2024年11月5日 LiuJinTao 文章目录 鸿蒙中动画的使用一、属性动画 - animation属性动画代码示例 二、显示动画 - AnimateTo三、专场动画 鸿蒙中动画的使用 一、属性动画 - animation 属性动画代码示例 /*** 属性动画的演示*/ Entry Component struct Index {State selfWidth:…

基于STM32的手式电视机遥控器设计

引言 本项目基于STM32微控制器设计了一个手式电视机遥控器系统&#xff0c;通过集成加速度传感器和陀螺仪&#xff0c;实现手势识别和遥控功能。该遥控器系统可以通过简单的手势操作实现对电视机的音量调节、频道切换和开关机控制等功能。项目涉及到硬件设计、手势识别算法和红…

基于SpringBoot+微信小程序+协同过滤算法+二维码订单位置跟踪的农产品销售平台-新

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; “农产品商城”小程序…

论文阅读-用于点云分析的自组织网络

目前存在的问题&#xff1a; 原始的SOM&#xff08;1&#xff09;训练结果与初始节点高度相关&#xff08;2&#xff09;样本更新规则取决于输入点的顺序3D 卷积神经网络&#xff08;需要将数据转换为体素&#xff0c;存在分辨率损失和计算成本上涨的问题&#xff09;、PointN…

ComfyUI和Photoshop相结合,PS内实现:文生图,图生图,高清放大,局部重绘,面部修复,设计师福音

本文主要介绍&#xff1a;ComfyUI和Photoshop相结合&#xff0c;一个平台实现&#xff1a;图像生成&#xff0c;放大&#xff0c;局部重绘&#xff0c;面部修复&#xff0c;实时绘画 简直是设计师的福音。 主要包括&#xff1a; Photoshop 的安装以及插件的安装 Creative Cl…

新一代跟踪器StrongSORT: Make DeepSORT Great Again论文解析—让 DeepSORT 再次伟大

新一代跟踪器StrongSORT: Make DeepSORT Great Again论文解析—让 DeepSORT 再次伟大 时间&#xff1a;2023年 机构:北京邮电大学 发表在&#xff1a;IEEE TRANSACTIONS ON MULTIMEDIA, VOL. 25, 2023 代码源码地址&#xff1a; pytorch版本&#xff1a;https://github.com/dyh…

【力扣专题栏】重排链表,如何实现链表里面节点之间的交换?

题解目录 1、题目描述解释2、算法原理解析3、代码编写 1、题目描述解释 主要就是实现&#xff1a;第一个节点和最后一个节点交换&#xff0c;第二节点和倒数第二个节点交换&#xff0c;依次交换下去。 2、算法原理解析 3、代码编写 class Solution { public:void reorderList(…

TP-LINK TL-XDN7000H免驱版 ubuntu 20.04驱动安装

最近装机购买的主板没有无线网卡&#xff0c;PCIE网卡因为显卡占位无法安装&#xff0c;无奈只能购买USB无限网卡。 1 网卡型号 TP-LINK WiFi6免驱900M usb无线网卡 外置高增益 台式机笔记本电脑wifi接收器发射器 TL-XDN7000H 2 驱动下载链接 TL-XDN7000H免驱版 V1.1 Linu…

【初阶数据结构与算法】复杂度分析练习之轮转数组(多种方法)

文章目录 复杂度练习之轮转数组方法1方法2方法3 总结 复杂度练习之轮转数组 题目链接&#xff1a;https://leetcode.cn/problems/rotate-array/description/    为什么我们把这道题作为复杂度的练习题呢&#xff1f;是因为我们以后做题都会涉及到复杂度的计算&#xff0c;我…