李维强-15级 发表于 2015-6-16 02:39:58

【MFC多线程】多线程里面WM_TIMER消息的响应

在项目中遇到的一系列用 VC多线程问题 总目录贴: (先留着 以后填上)

写在前面话:
该问题是我近期在实际上位机程序项目中遇到的,我把该问题重点提取出来,并且附上我自己的理解和解决思路。
特此记录下来为以后查阅起来做参考。


开发环境VC6.0SP6
运行环境XP SP3


问题描述:

一个基于对话框的程序:
下面是我的主要代码:

void CExample2Dlg::OnButton1()
{
        // TODO: Add your control notification handler code here3
        sum =0;
        uiTimer = SetTimer(1,10,NULL);
        AfxBeginThread(MyThread1,this);
}


UINT CExample2Dlg::MyThread1(void *param)
{
        CExample2Dlg *Dlg=(CExample2Dlg *)param;
        Sleep(105);

        CString tmp;
        tmp.Format("%d",Dlg->sum);
        AfxMessageBox(tmp);
        return 0;
}

void CExample2Dlg::OnTimer(UINT nIDEvent)
{
        // TODO: Add your message handler code here and/or call default
        sum++;
        CDialog::OnTimer(nIDEvent);
}



目的就是在按钮Button1点下过后,就开起定时器,在OnTimer里面做计数,再开启线程,然后在线程里面Sleep(105),然后再用AfxMessageBox(tmp);的方式显示出在Sleep(105)这段时间里面到底响应了多少次OnTimer;

我这里定时器周期设置的是10ms,我在线程里面Sleep(105),按照道理说,应该响应10次OnTimer,但是实际情况确是只响应了6,7次 这个次数不定 我想问问为什么?

我认为以上原因可能是启动线程需要一定时间,至于什么时候启动可能是操作系统自己分配的,

那么我把上面代码变一下,变成我在线程里面启动定时器,就不存在启动线程需要的时间干扰了吧


void CExample2Dlg::OnButton1()
{
        // TODO: Add your control notification handler code here3

        AfxBeginThread(MyThread1,this);
}


UINT CExample2Dlg::MyThread1(void *param)
{
        CExample2Dlg *Dlg=(CExample2Dlg *)param;
        Dlg->start();
        Sleep(110);

        CString tmp;
        tmp.Format("%d",Dlg->sum);
        AfxMessageBox(tmp);
        return 0;
}

void CExample2Dlg::OnTimer(UINT nIDEvent)
{
        // TODO: Add your message handler code here and/or call default
        sum++;
        CDialog::OnTimer(nIDEvent);
}


void CExample2Dlg::start()
{
        sum =0;
        uiTimer = SetTimer(1,10,NULL);
}



问题提出:

整个流程就是点击botton 就启动线程 然后线程里面启动定时器,然后线程sleep,让OnTimer响应,等线程Sleep完了,再输出响应了多少次OnTimer,可是这样 也就显示响应了7次,按理说,应该响应10次的,这是为什么?



李维强-15级 发表于 2015-6-16 02:46:46

问题分析以及解决

根据MSDN上面的解释,可以找到以下文字:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644906(v=vs.85).aspx

The time-out value, in milliseconds.
If uElapse is less than USER_TIMER_MINIMUM (0x0000000A), the timeout is set to USER_TIMER_MINIMUM. If uElapse is greater than USER_TIMER_MAXIMUM (0x7FFFFFFF), the timeout is set to USER_TIMER_MAXIMUM.


Timer使用的时间中断,
Windows中每隔1/18秒触发一个时钟中断,所以,Timer的定时精度1000/18
SetTimer线程优先级别很低,要等其它的线程执行完后运行它,所以精度度很差,用于要求不高的场合。如果要求高的话可以采用多媒体定时或更高级别的定时方法。

WM_TIMER消息的优先级比较低,当消息队列里没有其它待处理的高优先级的消息的时候,才会去处理它,如果你的应用程序很繁忙,那么延迟就会很明显。
The WM_TIMER message is a low-priority message. The GetMessage and PeekMessage functions post this message only when no other higher-priority messages are in the thread's message queue.

可以用以下代码验证:
#include <windows.h>

static int g_nCount = 0;
#define Timer_Once_Time (1000/18)
DWORD WINAPI threadFunc (LPVOID pArg)
{
        Sleep(100*Timer_Once_Time);
        printf("%d",*((int*)pArg));
        return 0;
}

void CALLBACK TimerProc(HWND hwnd, UINT message, UINT timerID, DWORD time)
{
        g_nCount++;
        printf("%d\n",g_nCount);
}

int _tmain(int argc, _TCHAR* argv[])
{
        HANDLE hThread;
        hThread = CreateThread (NULL, 0, threadFunc, &g_nCount, 0, NULL );
       

        SetTimer(NULL, 0, 10*Timer_Once_Time, TimerProc);
        // 主消息循环:
        MSG msg;
        while (GetMessage(&msg, NULL, 0, 0))
        {
                //if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
                {
                        TranslateMessage(&msg);
                        DispatchMessage(&msg);
                }
        }

        return (int) msg.wParam;
}

周鹏 发表于 2015-6-16 15:11:00

用时间戳最好,每次获取当前时间与时间戳比较

精度有各种选择,和选择的机制和数据类型有关

可以做到ms甚至ns级的定时

李维强-15级 发表于 2015-6-16 23:29:06

周鹏 发表于 2015-6-16 15:11
用时间戳最好,每次获取当前时间与时间戳比较

精度有各种选择,和选择的机制和数据类型有关


时间戳 是什么东西哟?我没见过也。。。

我为了更精确的定时,往往采用优先级高的定时器,只不过这次没有用到, 也解决了,用户要求没有这么高。。
页: [1]
查看完整版本: 【MFC多线程】多线程里面WM_TIMER消息的响应