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
相关文章:
C++笔试题之实现一个定时器
一.定时器(timer)的需求 1.执行定时任务的时,主线程不阻塞,所以timer必须至少持有一个线程用于执行定时任务 2.考虑到timer线程资源的合理利用,一个timer需要能够管理多个定时任务,所以timer要支持增删任务…...
【英特尔IA-32架构软件开发者开发手册第3卷:系统编程指南】2001年版翻译,2-13
文件下载与邀请翻译者 学习英特尔开发手册,最好手里这个手册文件。原版是PDF文件。点击下方链接了解下载方法。 讲解下载英特尔开发手册的文章 翻译英特尔开发手册,会是一件耗时费力的工作。如果有愿意和我一起来做这件事的,那么ÿ…...
快消零售行业的培训创新:构建在线培训知识库
在快速消费品(FMCG)行业中,员工的培训和发展对于保持竞争力至关重要。随着电子商务的兴起和消费者行为的变化,快消零售行业需要不断适应新的市场趋势。在线培训知识库作为一种有效的培训工具,可以帮助企业提升员工技能…...
【AI开源项目】Botpress - 开源智能聊天机器人平台及其部署方案
文章目录 Botpress 概述Botpress 的定位 Botpress 的主要特点1. OpenAI 集成2. 易于使用3. 定制和扩展性4. 多平台支持5. 集成和扩展 API6. 活跃的社区和详尽的文档 部署方案集成集成开发集成部署机器人示例开发工具代理本地开发先决条件从源代码构建 Botpress 如何解决常见问题…...
一文读懂系列:SSL加密流量检测技术详解
SSL加密流量检测功能的主要目的是为了对加密流量做解密处理,并对解密后的流量做内容安全检查(比如反病毒、入侵防御、URL远程查询、内容过滤、文件过滤和邮件过滤等)和审计(防止信息泄露)。接下来我们详细介绍SSL加密流…...
Android Studio各种历史版本
下载地址:AndroidDevTools - Android开发工具 Android SDK下载 Android Studio下载 Gradle下载 SDK Tools下载...
大数据导论及分布式存储HadoopHDFS入门
思维导图 数据导论 数据是什么? 进入21世纪,我们的生活就迈入了"数据时代" 作为21世纪的新青年,"数据"一词经常出现。 数据无时无刻的在影响着我们的现实生活 什么是数据? 数据又如何影响现实生活? 数据…...
语言模型的采样方法
语言模型的采样方法 语言模型的输出为一个向量,该向量的每一维代表着词典中对应词的概率。 在采用自回归范式的文本生成任务中,语言模型将依次生成一组向量并将其解码为文本。将这组向量解码为文本的过程被成为语言模型解码。 解码过程显著影响着生成文本…...
使用 Nginx 配置真实 IP 地址转发
使用 Nginx 配置真实 IP 地址转发 在许多 web 应用程序中,获取客户端的真实 IP 地址非常重要,尤其是在使用反向代理服务器(如 Nginx)时。本文将指导你如何在 Nginx 中配置 X-Real-IP 和 X-Forwarded-For 头部,以确保你…...
WPF+MVVM案例实战与特效(二十四)- 粒子字体效果实现
文章目录 1、案例效果2、案例实现1、文件创建2.代码实现3、界面与功能代码3、总结1、案例效果 提示:这里可以添加本文要记录的大概内容: 2、案例实现 1、文件创建 打开 Wpf_Examples 项目,在 Views 文件夹下创建窗体界面 ParticleWindow.xaml,在 Models 文件夹下创建粒子…...
Oracle视频基础1.4.3练习
15个视频 1.4.3 できない dbca删除数据库 id ls cd cd dbs ls ls -l dbca# delete a database 勾选 # chris 勾选手动删除数据库 ls ls -l ls -l cd /u01/oradata ls cd /u01/admin/ ls cd chris/ ls clear 初始化参数文件,admin,数据文件#新版本了…...
energy 发布 v2.4.5
更新内容 修复 energy cli install 命令安装开发环境 修复 动态库加载error未暴露 增加 JS ipc.on 监听模式,异步返回结果 修复 energy cli 不能强制退出问题 修复 MacOS 开发模式 debug 时不更新 helper 进程 优化 energy cli 在 MacOS 开发模式和安装包制作 link…...
一文详解工单管理系统,工单系统是什么意思
在现代企业管理中,工单管理系统已经成为提升效率和客户满意度的重要工具。随着企业规模的扩大和业务复杂性的增加,传统的手工工单处理方式已经无法满足企业的需求。本文将详细解析工单管理系统的定义、功能、优势,并推荐一款优秀的工单管理系…...
【无标题】基于SpringBoot的母婴商城的设计与实现
一、项目背景 当前社会各行业领域竞争压力非常大,随着当前时代的信息化,科学化发展,让社会各行业领域都争相使用新的信息技术,对行业内的各种相关数据进行科学化,规范化管理。这样的大环境让那些止步不前,…...
你需要了解的Android主题相关知识
在 Android 开发中,主题(Theme)是用于定义应用的视觉风格的一组样式集合。主题决定了应用的配色、字体样式、控件外观等,是整个应用的一致性视觉体验的重要组成部分。以下是对 Android 主题的全面介绍,包括主题的基础概…...
基于Multisim数控直流稳压电源电路(含仿真和报告)
【全套资料.zip】数控直流稳压电源电路设计Multisim仿真设计数字电子技术 文章目录 功能一、Multisim仿真源文件二、原理文档报告资料下载【Multisim仿真报告讲解视频.zip】 功能 1.输出直流电压调节范围5-12V。 2.输出电流0-500mA。 3.输出直流电压能步进调节,步…...
精读预告Bigtable
文章目录 1. 引言:2. 背景 1. 引言: 在本期的精读会中,我们将深入解读另一篇具有里程碑意义的论文——《Bigtable: A Distributed Storage System for Structured Data》。这篇论文详细介绍了 Bigtable 作为谷歌用于管理结构化数据的分布式存…...
软件架构演变:从单体架构到LLM链式调用
0 前言 软件架构——我们数字世界的蓝图——自20世纪中叶计算机时代诞生以来,已经发生了巨大演变。 20世纪60年代和70年代早期,以大型主机和单体软件为主导。而今天,数字领域已完全不同,运行在由云计算、API连接、AI算法、微服务…...
Redis-“自动分片、一定程度的高可用性”(sharding水平拆分、failover故障转移)特性(Sentinel、Cluster)
文章目录 零、写在前面一、水平拆分(sharding/分片)、故障转移(failover)机制介绍水平拆分(Sharding)故障转移机制 二、Redis的水平拆分的机制有关的配置1. 环境准备2. 配置文件配置3. 启动所有Redis实例4. 创建集群5. 测试集群读/写6. 集群管理 三、Red…...
操作系统(9) (并发-----原子性/互斥临界区/生产者消费者问题/临界区问题三条件/互斥性/进展性/公平性)
目录 1. 并发(Concurrency)的定义 2. 原子性(Atomicity) 3. 互斥(Mutual Exclusion) 4. 生产者-消费者问题(Producer-Consumer Problem) 5. 临界区Critical Section 6. 临界区问题…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
第三周 Day 3 🎯 今日目标 理解类(class)和对象(object)的关系学会定义类的属性、方法和构造函数(init)掌握对象的创建与使用初识封装、继承和多态的基本概念(预告) &a…...
