C++工具箱——定时器
Posted 牧秦丶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++工具箱——定时器相关的知识,希望对你有一定的参考价值。
有时候我们需要用到定时器这样一个东西,但是我们如果去一个窗口里面 SetTimer,但我们又需要在一个非 UI 类(线程)里要用计时器,那么解耦就没有办法实现了。有没有更好的办法呢?
答案是肯定的,我看可以写一个单件定时器类,用来管理定时控制,并且全局访问。你可能需要的知识有:单件模板类、Boost 等。我们期望的使用方式是:
/**
* \\file timer.h
* \\author arnozhang
* \\date 2012.8.13
* \\brief 系统计时器模块.
*/
#ifndef __TIMER_H__
#define __TIMER_H__
namespace Util
//
// 计时器回调函数.
//
typedef boost::function<void()> TimerCallback;
typedef boost::function<void()> AsyncCallProc;
/**
* 设置并启动一个计时器.
*
* \\param[in] timerID: 计时器 ID.
* \\param[in] nElapse: 计时器响应间隔.
* \\param[in] timerCbk: 计时器回调.
* \\param[in] bLoopTimer: 是否是循环计时器.
*
* \\remarks
* 当 bLoopTimer 为 false 时,计时器的回调函数只执行一次.
* 然后该计时器会从计时器管理器中删除.
*/
void SetTimerCallback(
void* timerID,
int nElapse,
TimerCallback timerCbk,
bool bLoopTimer = true
);
/**
* 清除一个计时器.
*
* \\param[in] timerID: 清除的计时器的 ID.
*
* \\remarks
* 如果计时器管理器中没有这个 ID 对应的计时器,
* 将什么都不做.否则删除计时器.
*/
void KillTimerCallback(void* timerID);
inline void AsyncCall(AsyncCallProc asyncProc)
int dummy = 0;
SetTimerCallback(&dummy, 500, asyncProc, false);
#define ASYNC_CALL_BIND(class_name, method_name) \\
boost::bind(&class_name::method_name, this)
#define TIMER_CALL_BIND(class_name, method_name) \\
ASYNC_CALL_BIND(class_name, method_name)
/*namespace Util ends here.*/
#endif /*__TIMER_H__*/
为什么我们要将 timerID 用 void* 表示呢?由于当定时器设置过多时,这个 ID 有可能重复,所以我们用一片内存的首地址来表示,尽量减小定时器的 ID 重复的可能性。我们还间接通过定时器实现了一个异步调用 AsyncCall 。
那么如何实现呢?首先,我们考虑到:
1、Timer 管理器要全局唯一,便于管理——用单件;
2、暴露的接口只有上述——实现全部放入 timer.cpp ;
3、计时器的 ID要尽量唯一,减小重复——考虑用回调类的this指针或者临时对象的地址;
4、任何类、线程均可使用该 Timer;
5、Timer 回调要简单易用——Boost::function 解决;
考虑到上述需求,我们在 timer.cpp 中的一个匿名命名空间(思考为什么不在Util 中)中定义一个单件类 CTimerMgr,并尽数实现 timer.h 中的类即可。走起:
#include "stdafx.h"
#include "timer.h"
#include "Singleton.h"
using namespace Util;
namespace
//
// 计时器管理器类.模块外不可见.
//
class CTimerMgr : public Singleton<CTimerMgr>
public:
struct TIME_ITEM
public:
void* timerID;
int nElapse;
TimerCallback timerCbk;
bool bLoop;
;
typedef map<void*, TIME_ITEM> TimerMap;
CTimerMgr()
_InitTimerMgr();
~CTimerMgr()
_UninitTimerMgr();
void SetTimerCallback(
void* timerID,
int nElapse,
TimerCallback timerCbk,
bool bLoopTimer
)
TIME_ITEM newItem =
timerID, nElapse, timerCbk, bLoopTimer
;
m_timers[timerID] = newItem;
::SetTimer(m_hTimerWnd, (UINT_PTR)timerID, nElapse, NULL);
void KillTimerCallback(void* timerID)
TimerMap::iterator iter = m_timers.find(timerID);
if (iter != m_timers.end())
::KillTimer(m_hTimerWnd, (UINT_PTR)timerID);
m_timers.erase(iter);
private:
void OnTimer(void* timerID)
TimerMap::iterator iter = m_timers.find(timerID);
if (iter != m_timers.end())
TIME_ITEM& item = iter->second;
TimerCallback cbk = item.timerCbk;
// 非循环Timer,删除之.
if (!item.bLoop)
::KillTimer(m_hTimerWnd, (UINT_PTR)timerID);
m_timers.erase(iter);
cbk();
static HRESULT CALLBACK OnTimerWndProc(
HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam
)
switch (uMsg)
case WM_TIMER:
void* timerID = reinterpret_cast<void*>(wParam);
CTimerMgr::GetInstance().OnTimer(timerID);
break;
default:
break;
return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
void _InitTimerMgr()
const WCHAR* const TIMER_CLS_NAME = L"__timer_cls_name";
const WCHAR* const TIMER_WND_NAME = L"__timer_wnd_name";
WNDCLASSEXW wndcls = sizeof(wndcls);
wndcls.hInstance = ::GetModuleHandle(NULL);
wndcls.lpszClassName = TIMER_CLS_NAME;
wndcls.hbrBackground = (HBRUSH)::GetStockObject(NULL_BRUSH);
wndcls.lpfnWndProc = &CTimerMgr::OnTimerWndProc;
wndcls.style = CS_HREDRAW | CS_VREDRAW;
::RegisterClassExW(&wndcls);
m_hTimerWnd = ::CreateWindowExW(
0,
TIMER_CLS_NAME,
TIMER_WND_NAME,
WS_OVERLAPPED,
0, 0, 0, 0,
HWND_MESSAGE,
NULL,
wndcls.hInstance,
NULL
);
void _UninitTimerMgr()
for (TimerMap::iterator iter = m_timers.begin();
iter != m_timers.end();
++iter
)
::KillTimer(m_hTimerWnd, (UINT_PTR)iter->first);
m_timers.clear();
private:
TimerMap m_timers;
HWND m_hTimerWnd;
;
/*namespace anonymous ends here.*/
void Util::SetTimerCallback(
void* timerID,
int nElapse,
TimerCallback timerCbk,
bool bLoopTimer /* = true */
)
CTimerMgr::GetInstance().SetTimerCallback(
timerID,
nElapse,
timerCbk,
bLoopTimer
);
void Util::KillTimerCallback(void* timerID)
CTimerMgr::GetInstance().KillTimerCallback(timerID);
我们在 CTimerMgr 内部维护了一个定时器信息的 map。该 map 用定时器的 ID 做键值。在 CTimerMgr 内实现了一个隐藏在内部的Windows 窗口,所有的计时器消息将送往该窗口的 WM_TIMER 处理函数。看起来没有一点难度。但功能非常强大:
#include "timer.h"
class CMyA
public:
CMyA() : m_value(0)
void f()
::SetTimerCallback(this, 1000, TIMER_CALL_BIND(CMyA, _timer_proc));
private:
void _timer_proc()
cout<<m_value++<<endl;
int m_value;
;
void _global_proc()
static int val = 0;
cout<<val++<<endl;
int WinMain(HINSTANCE, HINSTANCE, LPCTSTR, int)
// ...
int _dummy;
::SetTimerCallback(&_dummy, 1000, _global_proc);
// ...
return 0;
通过这个计时器,我们可以实现其他一些强大的功能,比如窗口动画、事件管理等等。下一章将讲解基于该 Timer 的窗口动画类的实现。
以上是关于C++工具箱——定时器的主要内容,如果未能解决你的问题,请参考以下文章