timer.hpp
#include <list>
#include <memory>
#include <mutex>
#include <boost/asio.hpp>
#include <boost/asio/deadline_timer.hpp>
typedef boost::system::error_code boost_error_code;
using namespace boost::asio;
using namespace boost;
using namespace std;
using IO_NUM = std::integral_constant<int, 32>;
class CQIoService : public asio::noncopyable
{
public:
using Ptr = std::shared_ptr<CQIoService>;
CQIoService(int concurrencyHint)
:
mTrickyIoService(std::make_shared<io_service>(concurrencyHint)),
mIoService(*mTrickyIoService)
{
}
CQIoService(io_service& ioService)
:
mIoService(ioService)
{}
virtual ~CQIoService()
{
stop();
}
void run() const
{
io_service::work worker(mIoService);
for (; !mIoService.stopped();)
{
mIoService.run();
}
}
void stop() const
{
mIoService.stop();
}
io_service& service() const
{
return mIoService;
}
auto io_callback(int timeout, std::function<void(void)> callback) const
{
std::chrono::nanoseconds timeout_(timeout);
auto timer = std::make_shared<asio::steady_timer>(mIoService);
timer->expires_from_now(timeout_);
timer->async_wait([timer, callback](const error_code& ec) {
if (!ec)
{
callback();
}
});
return timer;
}
public:
std::shared_ptr<io_service> mTrickyIoService;
io_service& mIoService;
};
class CQIoServiceThread : public asio::noncopyable
{
public:
using Ptr = std::shared_ptr<CQIoServiceThread>;
CQIoServiceThread(int concurrencyHint)
:
io_(concurrencyHint)
{
}
virtual ~CQIoServiceThread()
{
stop();
}
void start(size_t threadNum)
{
std::lock_guard<std::mutex> lck(mIoThreadGuard);
if (threadNum == 0)
{
throw std::runtime_error("thread num is zero");
}
if (!mIoThreads.empty())
{
return;
}
for (size_t i = 0; i < threadNum; i++)
{
mIoThreads.push_back(std::thread([this]() {
io_.run();
}));
}
}
void stop()
{
std::lock_guard<std::mutex> lck(mIoThreadGuard);
io_.stop();
for (auto& thread : mIoThreads)
{
try
{
thread.join();
}
catch (...)
{
}
}
mIoThreads.clear();
}
io_service& service()
{
return io_.service();
}
CQIoService& wrapperIoService()
{
return io_;
}
private:
CQIoService io_;
std::vector<std::thread> mIoThreads;
std::mutex mIoThreadGuard;
};
class CQIoServicePool : public asio::noncopyable
{
public:
using Ptr = std::shared_ptr<CQIoServicePool>;
CQIoServicePool(size_t poolSize,
int concurrencyHint)
:
mPickIoServiceIndex(0)
{
if (poolSize == 0)
{
throw std::runtime_error("pool size is zero");
}
for (size_t i = 0; i < poolSize; i++)
{
mIoServicePool.emplace_back(std::make_shared<CQIoServiceThread>(concurrencyHint));
}
}
virtual ~CQIoServicePool()
{
stop();
}
void set_threadNum_everyService(size_t threadNumEveryService)
{
std::lock_guard<std::mutex> lck(mPoolGuard);
for (const auto& service : mIoServicePool)
{
service->start(threadNumEveryService);
}
}
void stop()
{
std::lock_guard<std::mutex> lck(mPoolGuard);
for (const auto& service : mIoServicePool)
{
service->stop();
}
mIoServicePool.clear();
}
io_service& pickIoService()
{
const auto index = mPickIoServiceIndex.fetch_add(1, std::memory_order::memory_order_relaxed);
return mIoServicePool[index % mIoServicePool.size()]->service();
}
CQIoServiceThread::Ptr pickIoServiceThread()
{
const auto index = mPickIoServiceIndex.fetch_add(1, std::memory_order::memory_order_relaxed);
return mIoServicePool[index % mIoServicePool.size()];
}
private:
std::vector<CQIoServiceThread::Ptr> mIoServicePool;
std::mutex mPoolGuard;
std::atomic_int32_t mPickIoServiceIndex;
};
class CQIoServiceManager
{
public:
static CQIoServiceManager* obj()
{
static CQIoServiceManager instance;
return &instance;
}
CQIoServicePool::Ptr& get_io_service_pool() { return ios_; }
io_service& get_io_service() const { return ios_->pickIoService(); }
void stop() const { ios_->stop(); }
private:
CQIoServiceManager()
{
ios_ = std::make_shared<CQIoServicePool>(IO_NUM::value, 1);
ios_->set_threadNum_everyService(1);
}
~CQIoServiceManager() = default;
CQIoServicePool::Ptr ios_;
};
template<typename ObjType>
class CQTimer
{
using TimerCallBack = std::function<void(const boost_error_code&, ObjType&)>;
public:
CQTimer(int millseconds, const ObjType& t, const TimerCallBack& func)
:_timer(CQIoServiceManager::obj()->get_io_service()), _millseconds(millseconds), _isCancelled(false)
{
this->setTimer(t, func);
}
void cancel()
{
if (_isCancelled)
return;
_timer.cancel();
_isCancelled = true;
}
ObjType getObj() { return _t; }
private:
void start(const TimerCallBack& func)
{
_timer.expires_from_now(posix_time::milliseconds(_millseconds));
_timer.async_wait(std::bind(func, std::placeholders::_1, this->_t));
}
void setTimer(const ObjType& t, const TimerCallBack& func)
{
_t = t;
this->start(func);
}
private:
deadline_timer _timer;
int _millseconds;
ObjType _t;
bool _isCancelled;
};
template<typename ObjType>
class CQTimer_List
{
using CQTimer_Ptr = std::shared_ptr<CQTimer<ObjType>>;
// 这种定时器回调只有false(超时) 与 true(人为提前取消)两个状态
using TimerCallBackBool = std::function<void(bool, ObjType&)>;
// 这种定时器回调不仅可以知道是超时还是提前取消,还可以知道错误信息,可以用if来判断,行为上与上面那个接口一样
using TimerCallBackBsec = std::function<void(const boost_error_code&, ObjType&)>;
private:
std::mutex mu;
std::list<CQTimer_Ptr> timer_list;
TimerCallBackBool _tcbBool;
public:
void putInTimerList(const CQTimer_Ptr& t)
{
std::unique_lock<std::mutex> lock(mu);
timer_list.push_back(t);
}
// 如未查到相应对象,则返回空指针的对象
CQTimer_Ptr getFromTimerList(const ObjType& t, bool isLast = false)
{
std::unique_lock<std::mutex> lock(mu);
CQTimer_Ptr rc;
auto it = timer_list.begin();
for (; it != timer_list.end(); ++it)
{
if ((*it)->getObj() == t)
{
rc = *it;
timer_list.erase(it);
break;
}
}
return rc;
}
void timerCallBack(const boost_error_code& ec, ObjType& ot)
{
if (ec)_tcbBool(true, ot);
else _tcbBool(false, ot);
}
void runTimer(int timeout_ms, const ObjType& t, const TimerCallBackBool &func)
{
_tcbBool = func;
const TimerCallBackBsec func2 = std::bind(
&CQTimer_List::timerCallBack, this, std::placeholders::_1, std::placeholders::_2);
CQTimer_Ptr ot(new CQTimer<ObjType>(timeout_ms, t, func2));
putInTimerList(ot);//做定时器
}
void runTimer(int timeout_ms, const ObjType& t, const TimerCallBackBsec &func)
{
CQTimer_Ptr ot(new CQTimer<ObjType>(timeout_ms, t, func));
putInTimerList(ot);//做定时器
}
};
test.cpp
#include <iostream>
#include <thread>
#include "timer.hpp"
using namespace std;
using TYPE = int;
auto ctl = std::make_shared<CQTimer_List<TYPE>>();
using TimerCallBack = std::function<void(bool, TYPE)>;
void TimerCallBackHandle(bool rs, TYPE t)
{
if (rs) {
cout << "消息: "<< t << " 正常取消定时!" << endl;
return;
}
cout << "消息: " << t << " 超时了!" << endl;
}
void func()
{
for (int i = 10; i < 50; ++i) {
auto timer = ctl->getFromTimerList(i);
if (timer)timer->cancel();
}
}
int main()
{
const TimerCallBack tcb = TimerCallBackHandle;
// 对id:0-99999 进行定时
for (int i = 0; i < 100000; ++i) {
ctl->runTimer(3000, i, tcb);
}
// 取消部分id的定时
std::thread th(&func);
th.detach();
//程序保活
while(true) {
sleep(1000);
}
return 0;
}