VC++几种定时器使用方法的归纳

时间:2022-06-01 20:38:19

        在游戏编写中,我遇到一个现象:在连续按键的相应过程中,WM_TIMER消息一直没有得到相应。

        查阅网上的资料后发现,WM_TIMER的消息属于最低优先级的消息,当线程的队列中没有其他消息时,才检索该消息。而且设置的定时器不精确(大概57ms左右的精度),所以会出现了延迟的结果。于是,我想看看有没有其他的定时器方式,大致有以下几个方式:

        1.WM_TIMER:SetTimer,OnTimer,KillTimer这样的使用方式(最简单的);

        2.等待定时器(WaitableTimer):

        (1)一种是,多线程+WaitableTimer实现定时器功能(未使用响应函数的方法),相关代码如下:

int CWaitableTimerTestView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;

// TODO: 在此添加您专用的创建代码
// 创建定时器线程,并立即执行
m_hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)TimerThreadProc, (LPVOID)this, 0, &ThreadID);
return 0;
}
// 定时器线程执行函数
void TimerThreadProc(LPVOID lpArg)
{
// 多线程+WaitableTimer实现定时器功能(未使用定时响应函数版本)
CWaitableTimerTestView *pView = (CWaitableTimerTestView *)lpArg;
// 创建等待定时器
pView->m_hTimer = CreateWaitableTimer(NULL, FALSE, _T("MyWaitableTimer"));// 自动归零
if (NULL == pView->m_hTimer)
{
AfxMessageBox(_T("Can't create waitable timer!"));
return;
}
LARGE_INTEGER liDueTime;
liDueTime.QuadPart = -2000000LL;// 200ms=200*10^6ns=2*10^6(100ns)

// 设置等待定时器
if (FALSE == SetWaitableTimer(pView->m_hTimer, &liDueTime, 200, NULL, NULL, FALSE))
{
AfxMessageBox(_T("SetWaitableTimer Failed!"));
return;
}
while (pView->m_bTimerThreadRun)
{
if (WaitForSingleObject(pView->m_hTimer, INFINITE) != WAIT_OBJECT_0)
{
pView->MessageBox("WaitForSingleObject failed ");
}
else
{
pView->data++;
}
}
CancelWaitableTimer(pView->m_hTimer);
CloseHandle(pView->m_hTimer);

return;
}
        (2)另外一种是,多线程+WaitableTimer实现定时器功能(使用响应函数的方法),相关代码如下:

// 定时器线程执行函数
void TimerThreadProc(LPVOID lpArg)
{
// 多线程+WaitableTimer实现定时器功能(使用定时响应函数版本)
CWaitableTimerTestView *pView = (CWaitableTimerTestView *)lpArg;
// 创建等待定时器
pView->m_hTimer = CreateWaitableTimer(NULL, FALSE, _T("MyWaitableTimer"));// 自动归零
if (NULL == pView->m_hTimer)
{
AfxMessageBox(_T("Can't create waitable timer!"));
return ;
}
LARGE_INTEGER liDueTime;
liDueTime.QuadPart = -2000000LL;// 200ms=200*10^6ns=2*10^6(100ns)

while (pView->m_bTimerThreadRun)
{
// 设置等待定时器
if (FALSE == SetWaitableTimer(pView->m_hTimer, &liDueTime, 200, (PTIMERAPCROUTINE)TimerAPCProc, (LPVOID)lpArg, FALSE))
{
AfxMessageBox(_T("SetWaitableTimer Failed!"));
return;
}
else// 如果没有,就不会调用TimerAPCProc
{
SleepEx(
INFINITE, // Wait forever
TRUE); // Put thread in an alertable state
}
}
CancelWaitableTimer(pView->m_hTimer);
CloseHandle(pView->m_hTimer);
return;
}
// 远程调用
VOID CALLBACK TimerAPCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue)
{
CWaitableTimerTestView *pView = (CWaitableTimerTestView*)lpArg;
if (WaitForSingleObject(pView->m_hTimer, INFINITE) != WAIT_OBJECT_0)
{
pView->MessageBox("WaitForSingleObject failed ");
}
else
{
pView->data++;
}
}

        3.多线程+Sleep延时函数:(与2类似)

           另一个线程一直运行,每个一定时间运行一次,Sleep(mSec);

           注意此种方式在调用OnDraw(GetDC)方法时,会出现pDoc重复获取的assert不合法、闪烁及绘制错误等问题

// 定时器线程执行函数
void TimerThreadProc(LPVOID lpArg)
{
// 多线程+Sleep(msec)实现定时器功能
CWaitableTimerTestView *pView = (CWaitableTimerTestView *)lpArg;
while (pView->m_bTimerThreadRun)
{
pView->data++;
Sleep(200);
}
}

        4.采用多线程+WM_TIMER消息混合方式:

           多线程固定时间计算各图像元素的位置,而WM_TIMER通知定时绘图显示,在WM_KEYDOWN消息频繁时,由于WM_KEYDOWN处理过程中也要采用OnDraw方法重绘整个屏幕,故也可以达到定时移动背景和敌机相关的定时移动。
5.采用多媒体定时器,更加精确。(还未深入研究过)

        6.DirectX中的定时器(还未深入研究过)

以上是我目前为止查到能在MFC中使用的定时器方式,欢迎大家讨论补充,互相学习,共同进步!