当前位置: 首页 > news >正文

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++笔试题之实现一个定时器

一.定时器&#xff08;timer&#xff09;的需求 1.执行定时任务的时&#xff0c;主线程不阻塞&#xff0c;所以timer必须至少持有一个线程用于执行定时任务 2.考虑到timer线程资源的合理利用&#xff0c;一个timer需要能够管理多个定时任务&#xff0c;所以timer要支持增删任务…...

【英特尔IA-32架构软件开发者开发手册第3卷:系统编程指南】2001年版翻译,2-13

文件下载与邀请翻译者 学习英特尔开发手册&#xff0c;最好手里这个手册文件。原版是PDF文件。点击下方链接了解下载方法。 讲解下载英特尔开发手册的文章 翻译英特尔开发手册&#xff0c;会是一件耗时费力的工作。如果有愿意和我一起来做这件事的&#xff0c;那么&#xff…...

快消零售行业的培训创新:构建在线培训知识库

在快速消费品&#xff08;FMCG&#xff09;行业中&#xff0c;员工的培训和发展对于保持竞争力至关重要。随着电子商务的兴起和消费者行为的变化&#xff0c;快消零售行业需要不断适应新的市场趋势。在线培训知识库作为一种有效的培训工具&#xff0c;可以帮助企业提升员工技能…...

【AI开源项目】Botpress - 开源智能聊天机器人平台及其部署方案

文章目录 Botpress 概述Botpress 的定位 Botpress 的主要特点1. OpenAI 集成2. 易于使用3. 定制和扩展性4. 多平台支持5. 集成和扩展 API6. 活跃的社区和详尽的文档 部署方案集成集成开发集成部署机器人示例开发工具代理本地开发先决条件从源代码构建 Botpress 如何解决常见问题…...

一文读懂系列:SSL加密流量检测技术详解

SSL加密流量检测功能的主要目的是为了对加密流量做解密处理&#xff0c;并对解密后的流量做内容安全检查&#xff08;比如反病毒、入侵防御、URL远程查询、内容过滤、文件过滤和邮件过滤等&#xff09;和审计&#xff08;防止信息泄露&#xff09;。接下来我们详细介绍SSL加密流…...

Android Studio各种历史版本

下载地址&#xff1a;AndroidDevTools - Android开发工具 Android SDK下载 Android Studio下载 Gradle下载 SDK Tools下载...

大数据导论及分布式存储HadoopHDFS入门

思维导图 数据导论 数据是什么? 进入21世纪&#xff0c;我们的生活就迈入了"数据时代" 作为21世纪的新青年&#xff0c;"数据"一词经常出现。 数据无时无刻的在影响着我们的现实生活 什么是数据&#xff1f; 数据又如何影响现实生活&#xff1f; 数据…...

语言模型的采样方法

语言模型的采样方法 语言模型的输出为一个向量&#xff0c;该向量的每一维代表着词典中对应词的概率。 在采用自回归范式的文本生成任务中&#xff0c;语言模型将依次生成一组向量并将其解码为文本。将这组向量解码为文本的过程被成为语言模型解码。 解码过程显著影响着生成文本…...

使用 Nginx 配置真实 IP 地址转发

使用 Nginx 配置真实 IP 地址转发 在许多 web 应用程序中&#xff0c;获取客户端的真实 IP 地址非常重要&#xff0c;尤其是在使用反向代理服务器&#xff08;如 Nginx&#xff09;时。本文将指导你如何在 Nginx 中配置 X-Real-IP 和 X-Forwarded-For 头部&#xff0c;以确保你…...

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 初始化参数文件&#xff0c;admin&#xff0c;数据文件#新版本了…...

energy 发布 v2.4.5

更新内容 修复 energy cli install 命令安装开发环境 修复 动态库加载error未暴露 增加 JS ipc.on 监听模式&#xff0c;异步返回结果 修复 energy cli 不能强制退出问题 修复 MacOS 开发模式 debug 时不更新 helper 进程 优化 energy cli 在 MacOS 开发模式和安装包制作 link…...

一文详解工单管理系统,工单系统是什么意思

在现代企业管理中&#xff0c;工单管理系统已经成为提升效率和客户满意度的重要工具。随着企业规模的扩大和业务复杂性的增加&#xff0c;传统的手工工单处理方式已经无法满足企业的需求。本文将详细解析工单管理系统的定义、功能、优势&#xff0c;并推荐一款优秀的工单管理系…...

【无标题】基于SpringBoot的母婴商城的设计与实现

一、项目背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规范化管理。这样的大环境让那些止步不前&#xff0c;…...

你需要了解的Android主题相关知识

在 Android 开发中&#xff0c;主题&#xff08;Theme&#xff09;是用于定义应用的视觉风格的一组样式集合。主题决定了应用的配色、字体样式、控件外观等&#xff0c;是整个应用的一致性视觉体验的重要组成部分。以下是对 Android 主题的全面介绍&#xff0c;包括主题的基础概…...

基于Multisim数控直流稳压电源电路(含仿真和报告)

【全套资料.zip】数控直流稳压电源电路设计Multisim仿真设计数字电子技术 文章目录 功能一、Multisim仿真源文件二、原理文档报告资料下载【Multisim仿真报告讲解视频.zip】 功能 1.输出直流电压调节范围5-12V。 2.输出电流0-500mA。 3.输出直流电压能步进调节&#xff0c;步…...

精读预告Bigtable

文章目录 1. 引言&#xff1a;2. 背景 1. 引言&#xff1a; 在本期的精读会中&#xff0c;我们将深入解读另一篇具有里程碑意义的论文——《Bigtable: A Distributed Storage System for Structured Data》。这篇论文详细介绍了 Bigtable 作为谷歌用于管理结构化数据的分布式存…...

软件架构演变:从单体架构到LLM链式调用

0 前言 软件架构——我们数字世界的蓝图——自20世纪中叶计算机时代诞生以来&#xff0c;已经发生了巨大演变。 20世纪60年代和70年代早期&#xff0c;以大型主机和单体软件为主导。而今天&#xff0c;数字领域已完全不同&#xff0c;运行在由云计算、API连接、AI算法、微服务…...

Redis-“自动分片、一定程度的高可用性”(sharding水平拆分、failover故障转移)特性(Sentinel、Cluster)

文章目录 零、写在前面一、水平拆分(sharding/分片)、故障转移(failover)机制介绍水平拆分&#xff08;Sharding&#xff09;故障转移机制 二、Redis的水平拆分的机制有关的配置1. 环境准备2. 配置文件配置3. 启动所有Redis实例4. 创建集群5. 测试集群读/写6. 集群管理 三、Red…...

操作系统(9) (并发-----原子性/互斥临界区/生产者消费者问题/临界区问题三条件/互斥性/进展性/公平性)

目录 1. 并发&#xff08;Concurrency&#xff09;的定义 2. 原子性&#xff08;Atomicity&#xff09; 3. 互斥&#xff08;Mutual Exclusion) 4. 生产者-消费者问题&#xff08;Producer-Consumer Problem&#xff09; 5. 临界区Critical Section 6. 临界区问题&#xf…...

收藏备用!小白程序员必看:从基础到进阶,彻底吃透Prompt与提示工程

本文将从基础入门到进阶实操&#xff0c;全面拆解Prompt的核心知识点&#xff0c;涵盖概念定义、分类维度、核心要素、工作原理&#xff0c;以及可直接套用的实用提示工程方法。全程避开晦涩术语&#xff0c;用程序员易懂的表述搭配具体案例&#xff0c;适配刚接触大模型的小白…...

从混乱到有序:大数据规范性分析的转型之路

从混乱到有序:大数据规范性分析的转型之路 关键词:大数据分析、数据治理、规范性分析、数据质量、ETL流程、数据仓库、数据可视化 摘要:本文深入探讨了大数据分析从混乱无序状态向规范性分析转型的关键路径。文章首先分析了大数据环境下面临的典型数据质量问题,然后系统性地…...

FanControl完全攻略:智能风扇控制的动态平衡技术与多场景应用

FanControl完全攻略&#xff1a;智能风扇控制的动态平衡技术与多场景应用 【免费下载链接】FanControl.Releases This is the release repository for Fan Control, a highly customizable fan controlling software for Windows. 项目地址: https://gitcode.com/GitHub_Tren…...

MySQL服务从CentOS7迁移到Rocky Linux 9.4实施文档

一、文档概述 本文档针对CentOS 7系统上运行的MySQL服务迁移至Rocky Linux 9.4的完整实施流程进行说明,适用于生产环境下MySQL 5.7/8.0版本的迁移操作,涵盖迁移前准备、迁移执行、验证、回滚全流程,可有效控制迁移风险,保障业务连续性。 本次迁移可选两种方案:原地升级迁…...

BilibiliDown:如何轻松搞定B站视频下载与批量管理的完整指南

BilibiliDown&#xff1a;如何轻松搞定B站视频下载与批量管理的完整指南 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader &#x1f633; 项目地址: https://gitcode.com/gh_mir…...

避开PSRR仿真三大坑:用Cadence psspxf分析分频器时,这些设置错了白忙活

避开PSRR仿真三大坑&#xff1a;用Cadence psspxf分析分频器时&#xff0c;这些设置错了白忙活 在模拟电路设计的精密世界里&#xff0c;电源抑制比&#xff08;PSRR&#xff09;仿真是评估电路抗干扰能力的关键环节。许多工程师在完成基础仿真流程后&#xff0c;常会遇到结果异…...

概率论其实很简单:从“明天会不会下雨”到“AI怎么猜你心思”

一、概率就是“长期来看&#xff0c;这件事发生的比例”你早上看天气预报&#xff0c;说“降水概率30%”。你心想&#xff1a;才30%&#xff0c;不带了伞。结果下午淋成落汤鸡。你骂天气乱报&#xff1f;不用。因为30%的意思是&#xff1a;如果像今天这样的天气有100天&#xf…...

避开这5个坑!MES工艺路线管理中的常见错误及解决方案

避开这5个坑&#xff01;MES工艺路线管理中的常见错误及解决方案 在制造业数字化转型的浪潮中&#xff0c;MES&#xff08;制造执行系统&#xff09;已成为提升生产效率的关键工具。然而&#xff0c;许多企业在实施工艺路线管理模块时&#xff0c;常常陷入一些看似简单却影响深…...

4步解放炉石玩家:开源脚本工具从配置到精通全指南

4步解放炉石玩家&#xff1a;开源脚本工具从配置到精通全指南 【免费下载链接】Hearthstone-Script Hearthstone script&#xff08;炉石传说脚本&#xff09; 项目地址: https://gitcode.com/gh_mirrors/he/Hearthstone-Script 你是否也曾遇到这样的困境&#xff1a;每…...

Ubuntu 24.04 Noble Numbat 尝鲜记:用Docker搞定ROS 2 Humble开发环境(附镜像拉取与容器运行全流程)

Ubuntu 24.04 Noble Numbat 尝鲜记&#xff1a;用Docker搞定ROS 2 Humble开发环境&#xff08;附镜像拉取与容器运行全流程&#xff09; 当Ubuntu 24.04 Noble Numbat遇上ROS 2 Humble&#xff0c;就像两个来自不同时空的旅行者相遇——一个是最新发布的系统版本&#xff0c;另…...