WM_TIMER消息在线程被阻塞时的系统处理

    我的脑海中忽然对这样一个问题有一些模糊,也就是当一个安装了定时器的线程被阻塞期间,定时器消息如何被送往消息队列?在线程从阻塞状态恢复以后,消息队列的状态是怎么样的?是否里面聚集多个WM_TIMER消息?还是阻塞期间没有收到WM_TIMER消息,还是在阻塞期间多个应该送达的WM_TIMER被合并成了一个?(类似WM_PAINT消息那样)。

    所以我做了一个小实验来验证这个问题,结果我发现结论是最后一种情况,即可能系统在被唤起应该像某个线程的消息队列投递WM_TIMER消息时,它如果发现消息队列中已经有相同的WM_TIMER消息(ID号相同),则可能放弃投递,否则才会投递。这样就符合我们观察到的结果,即阻塞期间应该产生的多个定时器消息看起来仿佛被合并成了一个。

    这个试验是这样的,我给UI线程安装一个5秒钟间隔的定时器(收到定时器消息后在窗口进行输出),然后发起另一个线程,阻塞 UI 线程21秒的时间。然后观察UI线程的输出,效果如下:

    

    

 

    我们绘制一个更直观的的图形来解释上面的输出,如下:

    

        

 

    图中,红色的箭头是UI线程实际收到的定时器消息,蓝色箭头是阻塞期间应该产生消息的时间。可见,在阻塞期间应该产生 4 个 WM_TIMER 消息,但实际上在线程从阻塞状态恢复后,立即处理了仅仅一条。此后消息仍然按照既定间隔定期发送。

 

    上文中用于测试的代码如下:

 

 

CODE_WM_TIMER_TEST

#include "stdafx.h"
#include <string>
#include "resource.h"
using namespace std;

HINSTANCE hInst;
string m_msg;
int blockTime;

LRESULT CALLBACK MyDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
//测试线程
DWORD WINAPI TestThread(void* pArg);

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
     // TODO: Place code here.
    hInst = hInstance;
    DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_DIALOG1), NULL, (DLGPROC)MyDlgProc, 0);
    return 0;
}

// 对话框
LRESULT CALLBACK MyDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    static int b_timerOn;
    static int timerNum;
    switch(message)
    {
    case WM_INITDIALOG:
        b_timerOn = 0;
        timerNum = 0;
        break;
    case WM_COMMAND:
        {
            WORD ctl = LOWORD(wParam);
            switch(ctl)
            {
            case IDOK:
            case IDCANCEL:
                EndDialog(hDlg, ctl);
                return TRUE;
            case IDC_BT_SETTIMER:
                if(b_timerOn)
                {
                    SetDlgItemText(hDlg, ctl, "开始计时器");
                    KillTimer(hDlg, 1);
                }
                else
                {
                    SetDlgItemText(hDlg, ctl, "停止计时器");
                    SetTimer(hDlg, 1, 5000, NULL);
                }
                b_timerOn ^= 1; //取反(在0,1之间切换)
                return TRUE;

            case IDC_BT_STARTTHREAD:
                {
                    DWORD threadId;
                    SYSTEMTIME st;
                    blockTime = 21000;
                    char line[128];
                
                    HANDLE hThread = CreateThread(NULL, 0,
                        TestThread, 
                        (LPVOID)&blockTime,
                        0, //立即执行
                        &threadId
                        );

                    //sprintf(line, "thread: %ld start...\r\n", threadId);
                    //m_msg += line;
                    //SetDlgItemText(hDlg, IDC_MSG, m_msg.c_str());

                    //阻塞主线程
                    WaitForSingleObject(hThread, INFINITE);
                    CloseHandle(hThread);

                    GetLocalTime(&st);
                    sprintf(line, "%02d:%02d: thread: %ld exit...\r\n", st.wMinute, st.wSecond, threadId);
                    m_msg += line;
                    SetDlgItemText(hDlg, IDC_MSG, m_msg.c_str());
                }
                return TRUE;
            }
        }
        break;
    case WM_TIMER:
        {
            SYSTEMTIME st;
            char text[96];
            GetLocalTime(&st);
            sprintf(text, "%02d:%02d WM_TIMER_%04ld\r\n", st.wMinute, st.wSecond, timerNum);
            timerNum++;

            m_msg = m_msg + text;
            SetDlgItemText(hDlg, IDC_MSG, m_msg.c_str());
        }
        return TRUE;
    case WM_DESTROY:
        KillTimer(hDlg, 1);
        return TRUE;
    }
    return FALSE;
}

//测试线程
DWORD WINAPI TestThread(void* pArg)
{
    int* pBlockTime = (int*)pArg;
    Sleep(*pBlockTime);
    return 0;
}

 

    结论:根据上面的观察,可以认为,定时器消息和 绘制消息类似,在进程被阻塞期间,多个定时器消息可能被系统透明的合并成了一条消息。在从阻塞状态恢复后,定时器扔按照原有间隔继续发送。

 

    请注意,不要认为所有WM_TIMER会严格按照响应时间产生(即不要认为一定能产生相应的数量),不要认为每一条一定会在某个时刻得到处理(即其被处理的时间也取决于线程的运行状态,例如被阻塞所拖延)。可以认为在阻塞期间的所有定时器消息仅会在线程停止阻塞后处理一次。

 

 

 

时间: 2024-08-03 02:45:13

WM_TIMER消息在线程被阻塞时的系统处理的相关文章

当主线程崩溃而其它线程继续运行时发生什么

孤线程模式 症状 治疗和预防措施 总结 参考资料 关于作者 对本文的评价 当主线程崩溃而其它线程继续运行时发生什么? Eric E. Allen(eallen@cs.rice.edu) 博士研究生候选人,Rice 大学 2001 年 8 月 在多线程代码中,使用驱动其它线程所负责的动作的单个主线程是常见的.这个主线程发送消息,通常是通过把它们放到一个队列中,然后其它线程处理这些消息.但是如果主线程抛出一个异常,那么剩余的线程会继续运行,等待更多输入到该队列,导致程序冻结.在诊断 Java 代码的

Java线程的阻塞

线程的阻塞 线程的优先级 线程总是存在优先级,优先级范围在1~10之间,线程默认优先级是5(数值越大优先级越高): JVM线程调度程序是基于优先级的抢先调度机制: 在大多数情况下,当前运行的线程优先级将大于或等于线程池中任何线程的优先级 注意:当设计多线程应用程序的时候,一定不要依赖于线程的优先级.因为线程调度优先级操作是没有保障的,只能把线程优先级作用作为一种提高程序效率的方法,但是要保证程序不依赖这种操作. 阻塞方法 阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪).Java

从JDK源码角度看线程的阻塞和唤醒

        目前在Java语言层面能实现阻塞唤醒的方式一共有三种:suspend与resume组合.wait与notify组合.park与unpark组合.其中suspend与resume因为存在无法解决的竟态问题而被Java废弃,同样,wait与notify也存在竟态条件,wait必须在notify之前执行,假如一个线程先执行notify再执行wait将可能导致一个线程永远阻塞,如此一来,必须要提出另外一种解决方案,就是park与unpark组合,它位于JDK的juc包下,应该也是因为当时

java解析xml文件多个线程同时访问时内存溢出

问题描述 java解析xml文件多个线程同时访问时内存溢出,代码如下:解析的xml文件大小大概2M-10M,该怎么解决呢? String sourceFile = ConfigReader.get("data.store.path") + article.getSourceFile();org.dom4j.Document document = null;FileInputStream fin = null;try {fin = new FileInputStream(new File

消息队列 == 线程池 ? true : false

问题描述 消息队列 == 线程池 ? true : false 请问一下,消息队列是否和线程池相同,在看handler的详解时有点晕,弄不清,消息队列和线程池具体的功能了,请哪位大哥帮小弟,捋一捋,谢了! 解决方案 消息队列和线程池完全不同,消息队列通常是属于系统或者某个对象的,而线程池则是一种组织线程的手段,你可以写自己的线程调度算法来完成你的任务.所以两者是不同的东西. 解决方案二: 完全不同的东西,消息队列用于处理消息机制:线程池用于处理短期线程的业务

win32-WIN32编程断点无法命中WM_TIMER消息

问题描述 WIN32编程断点无法命中WM_TIMER消息 在WIN32编程中,设置了定时器,我在WM_Timer消息中打上断点,却无法命中断点,是怎么回事? 大家看看我的代码有没有问题? LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; RECT rect; int TIMER1 = 1; SYSTEMTIME st; static

mq-IBM MQ 中怎么区分作为客户端时第三方响应的消息和作为服务端时第三方发过来的请求消息

问题描述 IBM MQ 中怎么区分作为客户端时第三方响应的消息和作为服务端时第三方发过来的请求消息 求大神指教,mq是怎么区分当自己作为客户端时主动请求时服务端返回的消息 和自己作为服务端时别人的请求消息的?也就是说怎么不会把服务端返回的消息认为是第三方 主动请求的请求消息的 解决方案 http://www.cnblogs.com/flydragon0815/articles/4449842.html

Android动态显示当前年月日时分秒系统时间(示例代码)

在布局文件中放一个TextView用来显示时间,如下所示: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="

Windows 8中在安装VirtualBox时提示“系统找不到指定的路径”

  故障提示: Windows 8中在安装VirtualBox时提示"系统找不到指定的路径". 解决办法: 1. 单击开始菜单,在搜索框输入"CMD"命令打开命令提示符窗口; 2. 按住Shift键后右键单击VirtualBox文件选择"复制为路径"命令,接着,在命令提示符窗口右键单击选择"粘贴"命令; 3. 在命令后添加" -extract -path c:vbox",回车即可将安装文件解压到"