• windows定时器编程


    目前,Windows下的定时器编程主要有三种方式。

    1)SetTimer定时器是利用Windows窗口消息WM_TIMER来实现的。使用方法非常简单,SetTimer创建定时器,KillTimer销毁定时器。使用条件是调用线程必须要有窗口消息队列message queue,因此如果是工作线程就无法使用这种方法。


    2)WaitableTimer定时器,其实应该算是一种线程同步对象,CreateWaitableTimer创建定时器对象,SetWaitableTimer设置定时器回调函数,CLoseHandle销毁定时器。WaitableTimer可以跨线程、进程使用,只要知道定时器对象名字(创建定时器时设置)就可以控制该定时器对象了。WaitableTimer定时器的回调函数实际上是一个APC(Asynchronous Procedure Calls)异步过程调用函数。
    关于APC方面的知识可参考“谈谈对APC的一点理解”一文http://blog.csdn.net/wwwwly/archive/2009/07/10/4337907.aspx


    3)TimerQueueTimer定时器,应该算迄今为止Windows系统最强大的定时器了。他可以支持多种工作模式,而且定时精度也是最高的。
    使用时,首先要调用CreateTimerQueue创建一个定时器队列,然后用CreateTimerQueueTimer来创建一个TimerQueueTimer定时器,
    WT_EXECUTEDEFAULT,默认设置,回调函数将进入一个非I/O工作线程队列WT_EXECUTEINTIMERTHREAD,回调函数作为APC,在定时器线程中被调用,被调用的条件是线程进入可警告等待状态alertable wait status。仅适用于短时任务,否则可能会影响队列中的其他定时器。
    WT_EXECUTEINIOTHREAD,回调函数进入一个I/O工作线程队列,请注意,大多数定时器都需要调用线程进入可警告等待状态alertable wait status,并不是随随便便就能发生定时调用的。一个线程是否进入可警告等待状态可参见微软的说明http://msdn.microsoft.com/en-us/library/ms686307.aspx
    A thread goes into an alertable wait state by calling either SleepEx, MsgWaitForMultipleObjectsEx, WaitForSingleObjectEx, or WaitForMultipleObjectsEx, with the function's bAlertable parameter set to TRUE.
    所以希望定时器不受这种可警告等待状态的影响,最好是用TimerQueue来完成。

    等待定时器与用户定时器的区别

    1、等待定时器(SetWaitableTimer)与用户定时器(SetTimer)它们之间的最大差别是,用户定时器需要在应用程序中设置许多附加的用户界面结构,这使定时器变得资源更加密集。

    2、等待定时器属于内核对象,这意味着它们可以供多个线程共享,并且是安全的。

    用户定时器:用户定时器能够生成WM_TIMER消息,这些消息将返回给调用SetTimer(用于回调定时器)的线程和创建窗口(用于基于窗口的定时器)的线程。因此,当用户定时器报时的时候,只有一个线程得到通知。

    等待定时器:多个线程可以在等待定时器上进行等待,如果定时器是个人工重置的定时器,则可以调度若干个线程。

    如果要执行与用户界面相关的事件,以便对定时器作出响应,那么使用用户定时器来组织代码结构可能更加容易些,因为使用等待定时器时,线程必须既要等待各种消息,又要等待内核对象(如果要改变代码的结构,可以使用MsgaitForMultipleObjects函数)。

    等待定时器,当到了规定时间的时候,更有可能得到通知。WM_TIMER消息始终属于最低优先级的消息,当线程的队列中没有其他消息时,才检索该消息。等待定时器的处理方法与其他内核对象没有什么差别(PS:因此其使用与内核对象的使用一个样,这就是所谓的触类旁通吧),如果定时器发出报时信息,而你的线程正在等待之中,那么你的线程就会醒来。

    一个windows编程示例

    #include <iostream>
    #include <windows.h> 
    #include <stdio.h>
    
    using namespace std;
    
    static  int COUNT = 0;
    
    VOID CALLBACK TimerRoutine(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
    {
        if (NULL == lpParameter)
        {
            cout << "TimerRoutine parameter is NULL!" << endl;
            return;
        }
    
        printf("the values of param is :%d
    ", *(int*)lpParameter);
    }
    
    int main(int argc, TCHAR *argv[])
    {
        HANDLE m_hTimerQueueTimer = NULL;
        HANDLE m_hTimerQueue = NULL;
    
        while (NULL == (m_hTimerQueue = CreateTimerQueue())) {
            cout << "Create timer queue failed!" << endl;
            Sleep(10);
        }
    
        while (!CreateTimerQueueTimer(&m_hTimerQueueTimer, m_hTimerQueue, WAITORTIMERCALLBACK(TimerRoutine), &COUNT, 1000, 1000, NULL))
        {
            cout << "Create timer failed!" << endl;
            Sleep(10);
        }
    
        while (1) {
            Sleep(1000);
            COUNT++;
        }
    
        if (m_hTimerQueueTimer != NULL)
            DeleteTimerQueueTimer(m_hTimerQueue, m_hTimerQueueTimer, INVALID_HANDLE_VALUE);
        if (m_hTimerQueue != NULL)
            DeleteTimerQueueEx(m_hTimerQueue, INVALID_HANDLE_VALUE);
    
        m_hTimerQueueTimer = NULL;
        m_hTimerQueue = NULL;
    
    
        return 0;
    }

    参考:

    1.  Windows系统三种定时器的分析

    2. CreateTimerQueueTimer学习笔记 

    3. Sleep和 SleepEx函数

  • 相关阅读:
    CENTOS7下安装REDIS
    Linux 查看端口状态netstat
    Centos7启动zookeeper无法连接2181端口
    企业信息化之路---集成
    Linux启动/停止/重启Mysql数据库的方法
    详解线程池
    详细的RocketMQ说明
    2021面试题准备~~~
    Https原理详解
    es 常用DSL
  • 原文地址:https://www.cnblogs.com/embedded-linux/p/12018738.html
Copyright © 2020-2023  润新知