1.1、单次定时任务实现

boost 的asio库里有几个定时器,老的有 deadline_timer , 还有三个可配合 C++11 的 chrono 使用的 high_resolution_timer 、 steady_timer 和 system_timer 。deadline_timer是asio早期版本提供的定时器,使用boost.date_time库提供时间支持,deadline_timer会被逐渐淘汰。

仅仅固定时间后去执行某任务,可以使用boost中的asio库提供了steady_timer定时器,定时器的用法也是比较简单的,基本上分三步。创建 io_service , 创建timer 并设置等待时间, 调用wait 或async_wait 等待.

其中wait是同步等待,async_wait是异步等待,需要给一个回调给它。

具体实现如下:

#include <iostream>
#include <boost/asio/io_service.hpp>
#include <boost/asio/steady_timer.hpp>
#include <thread>void executeMission()
{std::cout<<"excute mission"<<std::endl;
}int main()
{boost::asio::io_context io_ctx;boost::asio::steady_timer asio_steady_timer{io_ctx,std::chrono::seconds{3}};asio_steady_timer.async_wait([](const boost::system::error_code &ec){std::cout<<"excute mission"<<std::endl;});//asio_steady_timer.async_wait(&executeMission);std::cout<<"start io_service"<<std::endl;io_ctx.run();return 0;
}

1.2、执行固定次数的定时任务

为了实现重复定时,需要在回调函数里修改steady_timer的过期时间,因此要把steady_timer传给回调函数:

#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/bind.hpp>
int count = 0;void executeMission(const boost::system::error_code& e, boost::asio::steady_timer *t)
{std::cout << "execute mission" << std::endl;if(count < 10) {++count;t->expires_at(t->expiry() + boost::asio::chrono::seconds(1));t->async_wait(boost::bind(executeMission, boost::asio::placeholders::error, t));}
}int main()
{boost::asio::io_context io_ctx;boost::asio::steady_timer t(io_ctx, boost::asio::chrono::seconds(1));t.async_wait(boost::bind(executeMission, boost::asio::placeholders::error, &t));io_ctx.run();std::cout << "time: " << count << std::endl;return 0;
}

用类封装下:

#include <iostream>
#include <chrono>
#include <thread>
#include <boost/asio.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/system_timer.hpp>class printer {private:boost::asio::io_context io_;boost::asio::steady_timer timer_;int count_;void print() {if (count_ < 500) {std::cout << count_ << "\n";++count_;timer_.expires_from_now(std::chrono::milliseconds (50));timer_.async_wait(std::bind(&printer::print, this));}else{std::cout << "Final count is " << count_ << "\n";delete this;}}void run() {timer_.expires_from_now(std::chrono::milliseconds (50));timer_.async_wait(std::bind(&printer::print, this));io_.run();}printer(): timer_(io_),count_(0) {}~printer() {}public:static printer* Create(){return new printer;}void start() {std::thread t;t = std::thread(std::mem_fn(&printer::run), this);t.detach();}
};
void foo()
{printer *p = printer::Create();p->start();
}
int main() {foo();std::cin.get();return 0;
}

输出:

....
490
491
492
493
494
495
496
497
498
499
Final count is 500

1.3、在多线程程序中处理定时回调(多线程处理多个定时任务)
由一个线程来调用io_context::run()导致了回调函数不能够并发的运行。为了避免这个限制,一个直接的方法就是用线程池来调用io_context::run()。然而,为了能够并发的处理回调,还需要一种方法来同步回调对共享的非线程安全的资源的访问。

同一个 io_context 可以同时给多个 timer使下

实例1:

#include <iostream>
#include <chrono>
#include <thread>
#include <boost/asio.hpp>#include <boost/asio/high_resolution_timer.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/system_timer.hpp>class printer2 {private:boost::asio::steady_timer timer_;int count_;void print() {if (count_ < 10) {std::cout << count_ << "\n";++count_;timer_.expires_from_now(std::chrono::milliseconds (500));timer_.async_wait(std::bind(&printer2::print, this));}else{std::cout << "Final count is " << count_ << "\n";delete this;}}printer2(boost::asio::io_context &io): timer_(io,std::chrono::milliseconds (500)),count_(0) {timer_.async_wait(std::bind(&printer2::print, this));}~printer2() {}public:static printer2* Create(boost::asio::io_context &io){return new printer2(io);}};int main() {boost::asio::io_context io;printer2::Create(io);printer2::Create(io);printer2::Create(io);printer2::Create(io);io.run();//boost::thread t1(boost::bind(&boost::asio::io_context::run, &io));std::cin.get();return 0;
}

输出;

......
7
7
7
7
8
8
8
8
9
9
9
9
Final count is 10
Final count is 10
Final count is 10
Final count is 10

实例2:

#include <iostream>
#include <chrono>
#include <boost/thread.hpp>
#include <boost/asio.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/system_timer.hpp>class Timer
{public:Timer(boost::asio::io_context& io_ctx, boost::asio::io_context::strand& strand_1, unsigned int timeout_, unsigned int id_): id(id_), count(0), timeout(timeout_), t(io_ctx, boost::asio::chrono::milliseconds(timeout_)), strand_(strand_1){t.async_wait(boost::asio::bind_executor(strand_, boost::bind(&Timer::OnTimerCallBack, this)));}private:void OnTimerCallBack(){if(count < 10) {++count;std::cout << " Id:" << id << " Count:" << count << std::endl;t.expires_at(t.expiry() + boost::asio::chrono::milliseconds(timeout));t.async_wait(boost::asio::bind_executor(strand_, boost::bind(&Timer::OnTimerCallBack, this)));}}private:unsigned int id;unsigned int count;unsigned int timeout;boost::asio::steady_timer t;boost::asio::io_context::strand& strand_;
};int main()
{boost::asio::io_context io_ctx;boost::asio::io_context::strand strand_(io_ctx);Timer timer1(io_ctx, strand_, 1000, 1);Timer timer2(io_ctx, strand_, 1000, 2);Timer timer3(io_ctx, strand_, 1000, 3);boost::thread t1(boost::bind(&boost::asio::io_context::run, &io_ctx));boost::thread t2(boost::bind(&boost::asio::io_context::run, &io_ctx));t1.join();t2.join();return 0;
}

1.4、循环执行定时任务

//boost::posix_time::to_simple_string函数需要这两个头文件
#include <boost/date_time.hpp>
#include <boost/date_time/posix_time/ptime.hpp>
//使用boost.chrono代替std.chrono,
#define BOOST_ASIO_DISABLE_STD_CHRONO
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/placeholders.hpp>
#include <boost/thread.hpp>class Timer
{
public:Timer() :work_(io_), timer_(io_){}
public:boost::thread_group thgp_;boost::asio::io_context io_;boost::asio::io_context::work work_;boost::asio::steady_timer timer_;
public:void init(){boost::system::error_code errCode;thgp_.create_thread(boost::bind(&boost::asio::io_service::run, boost::ref(io_), errCode));timer_.expires_from_now(boost::chrono::milliseconds(1000)); //设置过期时间长度timer_.async_wait(boost::bind(&Timer::excuteMission, this, boost::asio::placeholders::error));//异步等待std::cout << "initialize:" << localTime() << std::endl;//由Console可知, 函数立即返回了, 定时器的expires_from_now是由完成端口处理的}void stop(){timer_.cancel();  // 取消所有handlerwork_.~work();thgp_.join_all();std::cout << "Stop:" << localTime() << std::endl;}static std::string localTime(){return boost::posix_time::to_simple_string(boost::posix_time::microsec_clock::local_time());}void excuteMission(const boost::system::error_code& ec){std::cout<<"mission to print time:"<<localTime().c_str()<<" ErrorValue:"<<ec.value()<<" ErrorCode:"<<ec.message().c_str()<<std::endl;timer_.expires_from_now(boost::chrono::milliseconds(1000));timer_.async_wait(boost::bind(&Timer::excuteMission, boost::ref(*this), _1));
#if 0timer_.async_wait(boost::bind(&Timer::excuteMission, this, _1));timer_.async_wait(boost::bind(&Timer::excuteMission, this, boost::asio::placeholders::error));
#endif}
};int main(int argc, char** argv)
{Timer t;t.init();while(true){std::cout<<"execute other mission"<<std::endl;boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));}t.stop();std::cout << "press ENTER to exit..." << std::endl;//    std::cin.sync();return 0;
}

2、实现一个定时器执行定时任务

定时器一般支持单线程就够了,一般使用方法见下面代码。如果需要多线程怎么办,笔者一般用一个简单的办法:多线程的业务线程中不包含定时器管理器,单独启一个线程用来管理所有定时器,当时间触发时,向业务线程投递定时器消息即可。

2.1.最小堆实现

头文件min_heap.h

#ifndef TIMERMANAGER_H
#define TIMERMANAGER_H#include <vector>
#include <boost/function.hpp>
namespace MinHeap {class TimerManager;class Timer
{
public:enum TimerType { ONCE, CIRCLE };Timer(TimerManager& manager);~Timer();template<typename Fun>void Start(Fun fun, unsigned interval, TimerType timeType = CIRCLE);void Stop();private:void OnTimer(unsigned long long now);private:friend class TimerManager;TimerManager& manager_;TimerType timerType_;boost::function<void(void)> timerFun_;unsigned interval_;unsigned long long expires_;size_t heapIndex_;
};class TimerManager
{
public:static unsigned long long GetCurrentMillisecs();void DetectTimers();
private:friend class Timer;void AddTimer(Timer* timer);void RemoveTimer(Timer* timer);void UpHeap(size_t index);void DownHeap(size_t index);void SwapHeap(size_t, size_t index2);private:struct HeapEntry{unsigned long long time;Timer* timer;};std::vector<HeapEntry> heap_;
};template<typename Fun>
void Timer::Start(Fun fun, unsigned interval, TimerType timeType)
{Stop();interval_ = interval;timerFun_ = fun;timerType_ = timeType;expires_ = interval_ + TimerManager::GetCurrentMillisecs();manager_.AddTimer(this);
}}
#endif // TIMERMANAGER_H

源文件min_heap.cpp

#include "min_heap.h"
#define _CRT_SECURE_NO_WARNINGS
# include <sys/time.h>namespace MinHeap {Timer::Timer(TimerManager& manager): manager_(manager), heapIndex_(-1)
{
}Timer::~Timer()
{Stop();
}void Timer::Stop()
{if (heapIndex_ != -1){manager_.RemoveTimer(this);heapIndex_ = -1;}
}void Timer::OnTimer(unsigned long long now)
{if (timerType_ == Timer::CIRCLE){expires_ = interval_ + now;manager_.AddTimer(this);}else{heapIndex_ = -1;}timerFun_();
}// TimerManager
void TimerManager::AddTimer(Timer* timer)
{timer->heapIndex_ = heap_.size();HeapEntry entry = { timer->expires_, timer };heap_.push_back(entry);UpHeap(heap_.size() - 1);
}void TimerManager::RemoveTimer(Timer* timer)
{size_t index = timer->heapIndex_;if (!heap_.empty() && index < heap_.size()){if (index == heap_.size() - 1){heap_.pop_back();}else{SwapHeap(index, heap_.size() - 1);heap_.pop_back();size_t parent = (index - 1) / 2;if (index > 0 && heap_[index].time < heap_[parent].time)UpHeap(index);elseDownHeap(index);}}
}void TimerManager::DetectTimers()
{unsigned long long now = GetCurrentMillisecs();while (!heap_.empty() && heap_[0].time <= now){Timer* timer = heap_[0].timer;RemoveTimer(timer);timer->OnTimer(now);}
}void TimerManager::UpHeap(size_t index)
{size_t parent = (index - 1) / 2;while (index > 0 && heap_[index].time < heap_[parent].time){SwapHeap(index, parent);index = parent;parent = (index - 1) / 2;}
}void TimerManager::DownHeap(size_t index)
{size_t child = index * 2 + 1;while (child < heap_.size()){size_t minChild = (child + 1 == heap_.size() || heap_[child].time < heap_[child + 1].time)? child : child + 1;if (heap_[index].time < heap_[minChild].time)break;SwapHeap(index, minChild);index = minChild;child = index * 2 + 1;}
}void TimerManager::SwapHeap(size_t index1, size_t index2)
{HeapEntry tmp = heap_[index1];heap_[index1] = heap_[index2];heap_[index2] = tmp;heap_[index1].timer->heapIndex_ = index1;heap_[index2].timer->heapIndex_ = index2;
}unsigned long long TimerManager::GetCurrentMillisecs()
{
#ifdef _MSC_VER_timeb timebuffer;_ftime(&timebuffer);unsigned long long ret = timebuffer.time;ret = ret * 1000 + timebuffer.millitm;return ret;
#elsetimeval tv;::gettimeofday(&tv, 0);unsigned long long ret = tv.tv_sec;return ret * 1000 + tv.tv_usec / 1000;
#endif
}}

2.2、时间轮实现

头文件timer_wheel.h

#ifndef TIMERWHEEL_H
#define TIMERWHEEL_H
#pragma once
#include <list>
#include <vector>
#include <boost/function.hpp>namespace TimerWheel{class TimerManager;class Timer
{
public:enum TimerType {ONCE, CIRCLE};Timer(TimerManager& manager);~Timer();template<typename Fun>void Start(Fun fun, unsigned interval, TimerType timeType = CIRCLE);void Stop();private:void OnTimer(unsigned long long now);private:friend class TimerManager;TimerManager& manager_;TimerType timerType_;boost::function<void(void)> timerFun_;unsigned interval_;unsigned long long expires_;int vecIndex_;std::list<Timer*>::iterator itr_;
};class TimerManager
{
public:TimerManager();static unsigned long long GetCurrentMillisecs();void DetectTimers();private:friend class Timer;void AddTimer(Timer* timer);void RemoveTimer(Timer* timer);int Cascade(int offset, int index);private:typedef std::list<Timer*> TimeList;std::vector<TimeList> tvec_;unsigned long long checkTime_;
};template<typename Fun>
inline void Timer::Start(Fun fun, unsigned interval, TimerType timeType)
{Stop();interval_ = interval;timerFun_ = fun;timerType_ = timeType;expires_ = interval_ + TimerManager::GetCurrentMillisecs();manager_.AddTimer(this);
}
}
#endif // TIMERWHEEL_H

源文件timer_wheel.cpp

#include "timer_wheel.h"
#define _CRT_SECURE_NO_WARNINGS
# include <sys/time.h>
#define TVN_BITS 6
#define TVR_BITS 8
#define TVN_SIZE (1 << TVN_BITS)
#define TVR_SIZE (1 << TVR_BITS)
#define TVN_MASK (TVN_SIZE - 1)
#define TVR_MASK (TVR_SIZE - 1)
#define OFFSET(N) (TVR_SIZE + (N) *TVN_SIZE)
#define INDEX(V, N) ((V >> (TVR_BITS + (N) *TVN_BITS)) & TVN_MASK)namespace TimerWheel{
Timer::Timer(TimerManager& manager): manager_(manager), vecIndex_(-1)
{
}Timer::~Timer()
{Stop();
}void Timer::Stop()
{if (vecIndex_ != -1){manager_.RemoveTimer(this);vecIndex_ = -1;}
}void Timer::OnTimer(unsigned long long now)
{if (timerType_ == Timer::CIRCLE){expires_ = interval_ + now;manager_.AddTimer(this);}else{vecIndex_ = -1;}timerFun_();
}// TimerManager
TimerManager::TimerManager()
{tvec_.resize(TVR_SIZE + 4 * TVN_SIZE);checkTime_ = GetCurrentMillisecs();
}void TimerManager::AddTimer(Timer* timer)
{unsigned long long expires = timer->expires_;unsigned long long idx = expires - checkTime_;if (idx < TVR_SIZE){timer->vecIndex_ = expires & TVR_MASK;}else if (idx < 1 << (TVR_BITS + TVN_BITS)){timer->vecIndex_ = OFFSET(0) + INDEX(expires, 0);}else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)){timer->vecIndex_ = OFFSET(1) + INDEX(expires, 1);}else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)){timer->vecIndex_ = OFFSET(2) + INDEX(expires, 2);}else if ((long long) idx < 0){timer->vecIndex_ = checkTime_ & TVR_MASK;}else{if (idx > 0xffffffffUL){idx = 0xffffffffUL;expires = idx + checkTime_;}timer->vecIndex_ = OFFSET(3) + INDEX(expires, 3);}TimeList& tlist = tvec_[timer->vecIndex_];tlist.push_back(timer);timer->itr_ = tlist.end();--timer->itr_;
}void TimerManager::RemoveTimer(Timer* timer)
{TimeList& tlist = tvec_[timer->vecIndex_];tlist.erase(timer->itr_);
}void TimerManager::DetectTimers()
{unsigned long long now = GetCurrentMillisecs();while (checkTime_ <= now){int index = checkTime_ & TVR_MASK;if (!index &&!Cascade(OFFSET(0), INDEX(checkTime_, 0)) &&!Cascade(OFFSET(1), INDEX(checkTime_, 1)) &&!Cascade(OFFSET(2), INDEX(checkTime_, 2))){Cascade(OFFSET(3), INDEX(checkTime_, 3));}++checkTime_;TimeList& tlist = tvec_[index];TimeList temp;temp.splice(temp.end(), tlist);for (TimeList::iterator itr = temp.begin(); itr != temp.end(); ++itr){(*itr)->OnTimer(now);}}
}int TimerManager::Cascade(int offset, int index)
{TimeList& tlist = tvec_[offset + index];TimeList temp;temp.splice(temp.end(), tlist);for (TimeList::iterator itr = temp.begin(); itr != temp.end(); ++itr){AddTimer(*itr);}return index;
}unsigned long long TimerManager::GetCurrentMillisecs()
{
#ifdef _MSC_VER_timeb timebuffer;_ftime(&timebuffer);unsigned long long ret = timebuffer.time;ret = ret * 1000 + timebuffer.millitm;return ret;
#elsetimeval tv;::gettimeofday(&tv, 0);unsigned long long ret = tv.tv_sec;return ret * 1000 + tv.tv_usec / 1000;
#endif
}
}

2.3 定时器应用

main.cpp

#include <iostream>
#include <thread>
#include <chrono>
#include "timer_wheel.h"
#include "min_heap.h"
#include <chrono>void TimerHandler()
{std::chrono::steady_clock::duration d =std::chrono::steady_clock::now().time_since_epoch();std::chrono::microseconds mic = std::chrono::duration_cast<std::chrono::microseconds>(d);std::cout << "Timer:"<<mic.count() << std::endl;
}typedef void (*Function)();int main()
{
//    MinHeap::TimerManager tm;
//    MinHeap::Timer t(tm);TimerWheel::TimerManager tm;TimerWheel::Timer t(tm);t.Start(&TimerHandler, 1000);while (true){tm.DetectTimers();std::this_thread::sleep_for(std::chrono::milliseconds(1000));}std::cin.get();return 0;
}

输出:

Timer:6674738409
Timer:6675739056
Timer:6676739783
Timer:6677740959
Timer:6678746422
Timer:6679749721
Timer:6680751169
Timer:6681754799
Timer:6682754395
Timer:6683762516
^CPress <RETURN> to close this window...

参考:

https://www.cnblogs.com/lyqf365/p/4285166.html

https://zhuanlan.zhihu.com/p/31906251

C++定时器实现定时任务相关推荐

  1. Java 中Timer和TimerTask 定时器和定时任务使用的例子

    转载自  Java 中Timer和TimerTask 定时器和定时任务使用的例子 这两个类使用起来非常方便,可以完成我们对定时器的绝大多数需求 Timer类是用来执行任务的类,它接受一个TimerTa ...

  2. Quartz定时器与定时任务知识概括

    Quartz定时器与定时任务知识概括 定时任务调度 其他定时器 Quartz简介 Quartz简单入门 Spring和Quartz集成 SSMM和Quartz集成 Quartz集群 Quartz配置 ...

  3. java定时开始和关闭_springboot自带定时器实现定时任务的开启关闭以及定时时间可以配置详解...

    一.序言: 最近项目需要用到定时任务,需要完成一个定时功能.经过了解,项目中目前实现定时任务,一般有三种选择,一是用Java自带的timer类.稍微看了一下,可以实现大部分的指定频率的任务的调度(ti ...

  4. springboot自带定时器实现定时任务的开启关闭以及定时时间可以配置

    一.序言: 最近项目需要用到定时任务,需要完成一个定时功能.经过了解,项目中目前实现定时任务,一般有三种选择,一是用Java自带的timer类.稍微看了一下,可以实现大部分的指定频率的任务的调度(ti ...

  5. SpringBoot中使用定时器/开启定时任务

    1.启动类上添加@EnableScheduling注解 2.新建定时任务具体实现类 @Component public class RemindTime{//每隔10秒执行一次@Scheduled(c ...

  6. java定时器异常,定时任务异常 高手进

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 Error creating bean with name 'org.springframework.scheduling.quartz.Schedule ...

  7. Java的定时器Timer和定时任务TimerTask应用以及原理简析

    记录:272 场景:Java JDK自带的定时器Timer和定时任务TimerTask应用以及原理简析.在JDK工具包:java.util中可以找到源码,即java.util.Timer和java.u ...

  8. php定时器使用,PHP定时器的说明

    这篇文章主要介绍了关于PHP定时器的说明,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 常见的定时器有两种:一种周期性定时执行,例如每天的凌晨三点出报表:另一种在指定时间后执行(一次) ...

  9. 多线程、并发/并行、自定义线程类、线程安全、守护线程、定时器、线程状态、线程池

    目录 进程和线程: 进程: 线程: 多线程的好处: 线程调度: 分时调度: 抢占式调度: 并发与并行: 线程的生命周期: 实现线程的两种基本方式(还有第三种): 创建Thread线程类: 创建Runn ...

最新文章

  1. 转:在线框架引用 bootstrap/jq/jqmobile/css框架
  2. Excel常用英文字母快捷键:
  3. python线下培训-Python培训线上和线下有什么区别?
  4. 学习javascript必须订阅30个程序员的Blog
  5. java+spring+mysql配置_用spring的beans配置mysql数据库
  6. OpenGL编程轻松入门之二次几何体
  7. 2019蓝桥杯省赛---java---C---2(矩阵切割)
  8. Android系统的手表adb抓取log日志
  9. JavaScript常见设计模式梳理
  10. java开发autocad_.NET AutoCAD二次开发之路(四、文字篇)
  11. 2进制 , 8进制 , 10进制 , 16进制 , 介绍 及 相互转换 及 快速转换的方法
  12. 戴尔服务器安全模式进不去系统,无法进入系统win10戴尔电脑如何进入安全模式...
  13. Java面向对象编程——继承
  14. 1807520-99-5,DNP-PEG4-alcohol含有二硝基苯和羟基的PEG连接剂
  15. 【信奥赛一本通】 1413:确定进制(详细代码)
  16. linux C++ 获取当前时间,以标准时间“年-月-日 时:分:秒”的形式输出
  17. 换元积分法和分部积分法
  18. 一分钟实现动态模糊效果
  19. https://www.52pojie.cn/thread-688820-1-1.html
  20. 软件测试工程师必备的27个基础技能【快来看看有没有遗忘的】

热门文章

  1. 【转】微信公众号h5网页被嵌入广告 不知道什么原因
  2. CSS绘制气泡对话框样式(有边框)
  3. 机顶盒 img打包工具_安卓网络机顶盒如何通过KODI看m3u8电视直播
  4. 下拉推广系统立择火星推荐_下拉词推广权威易速达
  5. 京东数科七层负载 | HTTPS硬件加速 (Freescale加速卡篇)
  6. Python 爬虫json格式化输出
  7. 艺术与科技的狂欢,阿那亚2022砂之盒沉浸艺术季
  8. 接入商重构相关技术介绍
  9. Linux中,显示当前目录位置、列出当前目录下的目录和文件、切换或进入目录。
  10. Windows nginx 操作和配置