业务上需要实现一个简单的定时器,之前参考了CSDN上的帖子C++定时器,review和测试下来发现不能满足需求。需求是,提供启停接口,且要求停止时能迅速返回,而不是陷在上一轮休眠中。这个需求比较合理,因为显然不能使,停止定时器的时长依赖外部传入的定时周期。比如传入一个很大的周期,那可能停止耗时非常久。
实现原理比较简单,核心点如下:
- 启动一个线程做循环:执行任务+sleep(启动定时器)
- 循环可被外界打断(停止定时器)
- sleep可被外界打断(迅速停止,而不是陷在sleep中
“循环被外界打断”,可以用多线程+共享变量+锁;“sleep可被外界打断”,可以用条件变量的wait_for(lock, duration, predicate)接口。
#ifndef _TIMER_H_
#define _TIMER_H_
#include <functional>
#include <chrono>
#include <thread>
#include <atomic>
#include <memory>
#include <mutex>
#include <condition_variable>
class Timer
{
public:
Timer(): m_expired(true), m_try_to_expire(false)
{}
Timer(const Timer& timer)
{
m_expired = timer.m_expired.load();
m_try_to_expire = timer.m_try_to_expire.load();
}
~Timer()
{
stop();
}
void start(int interval, std::function<void()> task)
{
// is started, do not start again
if (m_expired == false)
return;
// start async timer, launch thread and wait in that thread
m_expired = false;
std::thread([this, interval, task]() {
while (!m_try_to_expire)
{
task();
std::unique_lock<std::mutex> locker(m_mutex);
m_expired_cond.wait_for(locker, std::chrono::milliseconds(interval), [] {return (m_try_to_expire == true);});
}
{
// timer be stopped, update the condition variable expired and wake main thread
std::lock_guard<std::mutex> locker(m_mutex);
m_expired = true;
m_expired_cond.notify_one();
}
}).detach();
}
void stop()
{
// do not stop again
if (m_expired)
return;
if (m_try_to_expire)
return;
//notify timer thread to stop
{
std::lock_guard<std::mutex> locker(m_mutex);
m_try_to_expire = true;
m_expired_cond.notify_one();
}
// wait until timer stop
{
std::unique_lock<std::mutex> locker(m_mutex);
m_expired_cond.wait(locker, [this] {return (m_expired == true);});
// reset the timer
if (m_expired == true)
m_try_to_expire = false;
}
}
private:
std::atomic<bool> m_expired; // timer stopped status
std::atomic<bool> m_try_to_expire; // timer is in stop process
std::mutex m_mutex;
std::condition_variable m_expired_cond;
};
#endif // !_TIMER_H_