my_time.cpp
#include "my_timer.h"
namespace MyCommon
{
CMyTimer::CMyTimer()
: m_mutex{}, m_cond{}, m_threadWorker{}, m_vecTask{}, m_msetTaskEvents{}, m_stackFreeIDs{}
{
std::unique_lock<std::mutex> lock(m_mutex);
m_bNeedStop = false;
m_threadWorker = std::thread([this] { Run(); });
}
CMyTimer::~CMyTimer()
{
std::unique_lock<std::mutex> lock(m_mutex);
m_bNeedStop = true;
lock.unlock();
m_cond.notify_all();
m_threadWorker.join();
m_vecTask.clear();
m_msetTaskEvents.clear();
while(!m_stackFreeIDs.empty())
{
m_stackFreeIDs.pop();
}
}
size_t CMyTimer::Add(const std::chrono::time_point<std::chrono::steady_clock> &when, std::function<void(size_t)> &&handler, const std::chrono::microseconds &period)
{
std::unique_lock<std::mutex> lock(m_mutex);
size_t id = 0;
// 添加一个新任务,如果freeid池没有id,需要新加入一个id
if(m_stackFreeIDs.empty()) {
id = m_vecTask.size();
TTask task(id, when, period, std::move(handler));
m_vecTask.push_back(std::move(task));
} else {
id = m_stackFreeIDs.top();
m_stackFreeIDs.pop();
TTask task(id, when, period, std::move(handler));
m_vecTask[id] = std::move(task);
}
m_msetTaskEvents.insert(TTaskEvent{when, id});
lock.unlock();
m_cond.notify_all();
return id;
}
size_t CMyTimer::Add(const uint64_t when, std::function<void(size_t)> &&handler, const uint64_t period )
{
return Add(std::chrono::microseconds(when), std::move(handler), std::chrono::microseconds(period));
}
bool CMyTimer::Remove(size_t id)
{
std::unique_lock<std::mutex> lock(m_mutex);
if(m_vecTask.size() == 0 || m_vecTask.size() <= id) {
return false;
}
m_vecTask[id].valid = false;
m_vecTask[id].handler = nullptr;
auto it = std::find_if(m_msetTaskEvents.begin(), m_msetTaskEvents.end(),
[&](const TTaskEvent &te) { return te.ref == id; });
if(it != m_msetTaskEvents.end()) {
m_stackFreeIDs.push(it->ref);
m_msetTaskEvents.erase(it);
}
lock.unlock();
m_cond.notify_all();
return true;
}
void CMyTimer::Run()
{
std::unique_lock<std::mutex> lock(m_mutex);
while(!m_bNeedStop) {
if(m_msetTaskEvents.empty()) {
// Wait for work
m_cond.wait(lock);
} else {
TTaskEvent te = *m_msetTaskEvents.begin();
if(std::chrono::steady_clock::now() >= te.next)
{
// Remove time event
m_msetTaskEvents.erase(m_msetTaskEvents.begin());
// Invoke the handler
lock.unlock();
m_vecTask[te.ref].handler(te.ref);
lock.lock();
if(m_vecTask[te.ref].valid && m_vecTask[te.ref].period.count() > 0)
{
// The event is valid and a periodic timer.
te.next += m_vecTask[te.ref].period;
m_msetTaskEvents.insert(te);
} else {
// The event is either no longer valid because it was removed in the
// callback, or it is a one-shot timer.
m_vecTask[te.ref].valid = false;
m_vecTask[te.ref].handler = nullptr;
m_stackFreeIDs.push(te.ref);
}
} else {
/* 阻塞当前线程,直到条件变量被唤醒,或直到抵达指定时间点 */
m_cond.wait_until(lock, te.next);
}
}
}
}
}; //end of namespace MyCommon
my_timer.h
#ifndef _MY_TIMER_H_
#define _MY_TIMER_H_
#include <algorithm>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <set>
#include <stack>
#include <thread>
#include <vector>
namespace MyCommon
{
// The event structure that holds the information about a timer.
typedef struct TagTask
{
size_t id;
std::chrono::time_point<std::chrono::steady_clock> start;
std::chrono::microseconds period;
std::function<void(size_t)> handler;
bool valid;
TagTask()
: id(0), start(std::chrono::microseconds::zero()), period(std::chrono::microseconds::zero()), handler(nullptr), valid(false)
{
}
template <typename Func>
TagTask(size_t id, std::chrono::time_point<std::chrono::steady_clock> start, std::chrono::microseconds period, Func &&handler)
: id(id), start(start), period(period), handler(std::forward<Func>(handler)), valid(true)
{
}
TagTask(TagTask &&r) = default;
TagTask &operator=(TagTask &&ev) = default;
TagTask(const TagTask &r) = delete;
TagTask &operator=(const TagTask &r) = delete;
}TTask;
// 定时器结构体 ,记录下一次超时的时间点 and a reference to its
// Event struct.
typedef struct TagTaskEvent {
std::chrono::time_point<std::chrono::steady_clock> next;
size_t ref;
bool operator<( const TagTaskEvent &r) const
{
return this->next < r.next;
}
}TTaskEvent;
class CMyTimer
{
public:
public:
CMyTimer();
virtual ~CMyTimer();
/**
* 添加一个定时器.
*
* \param when 到达该时间点时,开始执行定时器.
* \param handler 定时器触发时,调用本函数回调.
* \param period 计时器触发的周期。仅用于周期计时器.
*/
size_t Add(const std::chrono::time_point<std::chrono::steady_clock> &when, std::function<void(size_t)> &&handler, const std::chrono::microseconds &period = std::chrono::microseconds::zero());
/**
* 重载的“add”函数,在第一次超时时使用“std::chrono::duration”而不是“time_point”。
* 模板类不能分离头文件,所有的代码必须放在一起,编译器不会查找cpp文件,只会在声明方法的文件内查找定义
*/
template <class Rep, class Period>
size_t Add(const std::chrono::duration<Rep, Period> &when, std::function<void(size_t)> &&handler, const std::chrono::microseconds &period = std::chrono::microseconds::zero())
{
return Add(std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::microseconds>(when),
std::move(handler), period);
}
/**
* 重载的“add”函数,在第一次超时和期间使用uint64_t而不是“time_point”。
*/
size_t Add(const uint64_t when, std::function<void(size_t)> &&handler, const uint64_t period = 0);
/**
* 根据id移除定时器
*/
bool Remove(size_t id);
private:
void Run();
private:
// 线程和锁的变量.
std::mutex m_mutex;
std::condition_variable m_cond;
std::thread m_threadWorker;
// 需要停止定时器线程.
bool m_bNeedStop = false;
// 保存所有活动任务。重复使用,程序退出前不会销毁,因为使用了vector的下标和size作为id
std::vector<TTask> m_vecTask;
// 排序集合,顶部有下一个超时的任务
std::multiset<TTaskEvent> m_msetTaskEvents;
// id 池.
std::stack<size_t> m_stackFreeIDs;
};
};//end of namespace MyCommon
#endif
伪代码demo
class CTest
{
public:
CTest();
~CTest();
/* 开始 */
int Start()
{
if(!m_pTimer)
{
std::unique_ptr<MyCommon::CMyTimer> pTimer(new MyCommon::CMyTimer());
m_pTimer = std::move(pTimer);
/* 定时器,1秒钟触发一次 */
m_nTimerID = m_pTimer->Add(std::chrono::milliseconds(0), CTest::OnRtcpTimer, std::chrono::microseconds(1000000));
m_pTimer->Add(std::chrono::milliseconds(1000), [&](std::size_t){
print();
std::cout<<" ... print ..."<<std::endl;
},std::chrono::microseconds(1000*1000));
}
}
/* 停止 */
int Stop()
{
if(!m_pTimer)
{
m_pTimer->Remove(m_nTimerID);
}
}
private:
static void OnRtcpTimer(size_t id)
{
std::cout<<"CMyRtcpManager::OnRtcpTimer ......"<<std::endl;
}
void print()
{
std::cout<<"CMyRtcpManager::print ......"<<std::endl;
}
std::unique_ptr<MyCommon::CMyTimer> m_pTimer;
/* 定时器ID */
size_t m_nTimerID;
}
int main()
{
CTest test;
getchar();
return 0;
}