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
#include
#include
#include
void executeMission()
{
std::cout<<"excute mission"<
1.2、执行固定次数的定时任务
为了实现重复定时,需要在回调函数里修改steady_timer的过期时间,因此要把steady_timer传给回调函数:
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
#include
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
#include
//使用boost.chrono代替std.chrono,
#define BOOST_ASIO_DISABLE_STD_CHRONO
#include
#include
#include
#include
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(); // 取消所有handler
work_.~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:"<
2、实现一个定时器执行定时任务
定时器一般支持单线程就够了,一般使用方法见下面代码。如果需要多线程怎么办,笔者一般用一个简单的办法:多线程的业务线程中不包含定时器管理器,单独启一个线程用来管理所有定时器,当时间触发时,向业务线程投递定时器消息即可。
2.1.最小堆实现
头文件min_heap.h
#ifndef TIMERMANAGER_H
#define TIMERMANAGER_H
#include
#include
namespace MinHeap {
class TimerManager;
class Timer
{
public:
enum TimerType { ONCE, CIRCLE };
Timer(TimerManager& manager);
~Timer();
template
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 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 heap_;
};
template
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
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);
else
DownHeap(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;
#else
timeval 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
#include
#include
namespace TimerWheel{
class TimerManager;
class Timer
{
public:
enum TimerType {ONCE, CIRCLE};
Timer(TimerManager& manager);
~Timer();
template
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 timerFun_;
unsigned interval_;
unsigned long long expires_;
int vecIndex_;
std::list::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 TimeList;
std::vector tvec_;
unsigned long long checkTime_;
};
template
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
#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;
#else
timeval tv;
::gettimeofday(&tv, 0);
unsigned long long ret = tv.tv_sec;
return ret * 1000 + tv.tv_usec / 1000;
#endif
}
}
2.3 定时器应用
main.cpp
#include
#include
#include
#include "timer_wheel.h"
#include "min_heap.h"
#include
void TimerHandler()
{
std::chrono::steady_clock::duration d =
std::chrono::steady_clock::now().time_since_epoch();
std::chrono::microseconds mic = std::chrono::duration_cast(d);
std::cout << "Timer:"<
输出:
Timer:6674738409
Timer:6675739056
Timer:6676739783
Timer:6677740959
Timer:6678746422
Timer:6679749721
Timer:6680751169
Timer:6681754799
Timer:6682754395
Timer:6683762516
^CPress to close this window...
参考:
https://www.cnblogs.com/lyqf365/p/4285166.html
https://zhuanlan.zhihu.com/p/31906251