我的脑海中忽然对这样一个问题有一些模糊,也就是当一个安装了定时器的线程被阻塞期间,定时器消息如何被送往消息队列?在线程从阻塞状态恢复以后,消息队列的状态是怎么样的?是否里面聚集多个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会严格按照响应时间产生(即不要认为一定能产生相应的数量),不要认为每一条一定会在某个时刻得到处理(即其被处理的时间也取决于线程的运行状态,例如被阻塞所拖延)。可以认为在阻塞期间的所有定时器消息仅会在线程停止阻塞后处理一次。

转载于:https://www.cnblogs.com/hoodlum1980/archive/2010/08/20/1804913.html

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

  1. linux线程并不真正并行,Linux系统编程学习札记(十二)线程1

    Linux系统编程学习笔记(十二)线程1 线程1: 线程和进程类似,但是线程之间能够共享更多的信息.一个进程中的所有线程可以共享进程文件描述符和内存. 有了多线程控制,我们可以把我们的程序设计成为在一 ...

  2. 聊聊JVM(九)理解进入safepoint时如何让Java线程全部阻塞

    在这篇聊聊JVM(六)理解JVM的safepoint 中说了safepoint的基本概念,VM thread在进行GC前,必须要让所有的Java线程阻塞,从而stop the world,开始标记.J ...

  3. python线程池阻塞队列_福利又来啦!python多线程进阶篇

    使用Python中的线程模块,能够同时运行程序的不同部分,并简化设计.如果你已经入门Python,并且想用线程来提升程序运行速度的话,希望这篇教程会对你有所帮助. 通过阅读本文,你将了解到:什么是死锁 ...

  4. 消息循环中的TranslateMessage函数和DispatchMessage函数,特别注意WM_TIMER消息

    TranslateMessage函数 函数功能描述:将虚拟键消息转换为字符消息.字符消息被送到调用线程的消息队列中,在下一次线程调用函数GetMessage或PeekMessage时被读出. .函数原 ...

  5. 探究并发和并行、同步和异步、进程和线程、阻塞和非阻塞、响应和吞吐等

    一. 并发和并行 操作系统扫盲: 1. 对于单核cpu而言(不管单核单线程也好,单核多线程也罢),同一时间只能干一件事!!为了看起像可以"同时干多件事",windows操作系统把c ...

  6. 线程的挂起是错误的概念实际是线程的阻塞,挂起只针对进程,将进程挂起会将进程从内存空间交换到磁盘空间的过程

    线程的挂起是错误的概念实际是线程的阻塞 线程的主要状态有运行态,就绪态和阻塞态.挂起态对线程没有什么意义,这是由于此类状态是一个进程级的概念.特别地,如果一个进程被换出,由于它的所有线程都该进程的地址 ...

  7. 当进度对话框和后台线程处于活动状态时,如何处理屏幕方向变化?

    我的程序在后台线程中执行一些网络活动. 在开始之前,它会弹出一个进度对话框. 该对话框在处理程序上关闭. 这一切都可以正常工作,除非对话框打开(并且背景线程正在运行)时屏幕方向发生变化. 此时,应用程 ...

  8. 【Java 并发编程】线程池机制 ( 线程池阻塞队列 | 线程池拒绝策略 | 使用 ThreadPoolExecutor 自定义线程池参数 )

    文章目录 一.线程池阻塞队列 二.拒绝策略 三.使用 ThreadPoolExecutor 自定义线程池参数 一.线程池阻塞队列 线程池阻塞队列是线程池创建的第 555 个参数 : BlockingQ ...

  9. 【Android 异步操作】手写 Handler ( Message 消息 | ThreadLocal 线程本地变量 | Looper 中的消息队列 MessageQueue )

    文章目录 一.Message 消息 二.ThreadLocal 线程本地变量 三.Looper 中的消息队列 MessageQueue 一.Message 消息 模仿 Android 中的 Messa ...

最新文章

  1. 10篇Nature专题报导人类微生物组计划2(iHMP)成果及展望
  2. 并发编程不是少数派技能,每个程序员都要尝试掌握
  3. NFX UNISTACK
  4. eureka知识点概览
  5. python中if语句and和or用法_python中if语句的使用(比较、逻辑、成员、运算符,语句结构)...
  6. iOS项目开发— CoreLocation的定位服务和地理编码与发编码实现
  7. 倒计时小工具_这款高颜值的 APP 可以让小仙女/男神们的日子过得更精致
  8. 【机器学习PAI实战】—— 玩转人工智能之美食推荐
  9. 第三回 Bootstrap3.x 起步
  10. CNN————激活可能性小的神经元不代表这个神经元的用处小
  11. 台大李宏毅Machine Learning 2017Fall学习笔记 (5)Classification: Probabilistic Generative Model
  12. Windows系统安装jdk1.6
  13. JavaWeb实训项目案例开发之在线图书网站开发【非常适合初学者】
  14. 理查德·费曼:发现的乐趣
  15. OneNote 英文默认字体修改方法(2020.10)
  16. 神经网络中的权重初始化问题weight initialization problem in FNN
  17. 晶振电路的PCB设计
  18. 使用ale-import-roms导入atari的rom时RuntimeError问题解决办法
  19. 致我们渐行渐远的青春——给曾经的你
  20. 【RF分类】基于matlab随机森林算法数据分类【含Matlab源码 2048期】

热门文章

  1. 从零开始刷Leetcode——数组(118.119.121)
  2. 最新发布!《统计学习方法》第二版无监督学习视频课上线了!
  3. signature=e5535ff98b93aa63c455611822dc57c2,高校新生预激综合征6例报告
  4. php 读取数据库信息,php读取数据库信息的几种方法
  5. mysql连接查询_.net core 里连接mysql查询数据的方法
  6. 基于python的文件加密传输_Python优雅的加密传输文件
  7. 修改Ubuntu下的jenkins端口号
  8. 用GDB调试PHP扩展
  9. HttpURLConnection请求数据流的写入(write)和读取(read)
  10. struts1.x 错误之 java.lang.IllegalArgumentException: No bean specified