目前,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,并不是随随便便就能发生定时调用的。一个线程是否进入可警告等待状态可参见微软的说明SleepEx function (synchapi.h) - Win32 apps | Microsoft Learn
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\n", *(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; }
参考:
用户感觉到软件的好用,就是可以定时地做一些工作,而不需要人参与进去。比如每天定时地升级病毒库,定时地下载电影,定时地更新游戏里的人物。要想实现这些功能,就可以使用定时器的 API 函数 CreateWaitableTimer 和 SetWaitableTimer 来实现了,这对 API 函数创建的时钟是比较精确的,可以达到 100 倍的 10 亿分之一秒。
函数 CreateWaitableTimer 和 SetWaitableTimer 声明如下:
WINBASEAPI
__out
HANDLE
WINAPI
CreateWaitableTimerA(
__in_opt LPSECURITY_ATTRIBUTES lpTimerAttributes,
__in BOOL bManualReset,
__in_opt LPCSTR lpTimerName
);
WINBASEAPI
__out
HANDLE
WINAPI
CreateWaitableTimerW(
__in_opt LPSECURITY_ATTRIBUTES lpTimerAttributes,
__in BOOL bManualReset,
__in_opt LPCWSTR lpTimerName
);
#ifdef UNICODE
#define CreateWaitableTimer CreateWaitableTimerW
#else
#define CreateWaitableTimer CreateWaitableTimerA
#endif // !UNICODE
WINBASEAPI
BOOL
WINAPI
SetWaitableTimer(
__in HANDLE hTimer,
__in const LARGE_INTEGER *lpDueTime,
__in LONG lPeriod,
__in_opt PTIMERAPCROUTINE pfnCompletionRoutine,
__in_opt LPVOID lpArgToCompletionRoutine,
__in BOOL fResume
);
lpTimerAttributes 是设置定时器的属性。
bManualReset 是是否手动复位。
lpTimerName 是定时器的名称。
hTimer 是定时器的句柄。
lpDueTime 是设置定时器时间间隔,当设置为正值是绝对时间;当设置为负数是相对时间。
lPeriod 是周期。
pfnCompletionRoutine 是设置回调函数。
lpArgToCompletionRoutine 是传送给回调函数的参数。
fResume 是设置系统是否自动恢复。
调用函数的例子如下:
#001 // 创建定时器
#002 // 蔡军生 2007/11/06 QQ:9073204 深圳
#003 int CreateTestTimer(void)
#004 {
#005 HANDLE hTimer = NULL;
#006 LARGE_INTEGER liDueTime;
#007
#008 // 设置相对时间为 10 秒。
#009 liDueTime.QuadPart = -100000000;
#010
#011 ; //创建定时器。
#012 hTimer = CreateWaitableTimer(NULL, TRUE, _T("TestWaitableTimer"));
#013 if (!hTimer)
#014 {
#015 return 1;
#016 }
#017
#018 OutputDebugString(_T("10秒定时器/r/n"));
#019
#020 // 设置10秒钟。
#021 if (!SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0))
#022 {
#023 //
#024 CloseHandle(hTimer);
#025 return 2;
#026 }
#027
#028 //等定时器有信号。
#029 if (WaitForSingleObject(hTimer, INFINITE) != WAIT_OBJECT_0)
#030 {
#031 OutputDebugString(_T("10秒定时器出错了/r/n"));
#032 //
#033 CloseHandle(hTimer);
#034 return 3;
#035 }
#036 else
#037 {
#038 //10秒钟到达。
#039 OutputDebugString(_T("10秒定时器到了/r/n"));
#040 }
#041
#042 //
#043 CloseHandle(hTimer);
#044 return 0;
#045 }
再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希
————————————————
版权声明:本文为CSDN博主「这是晾衣服的地方吗」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ffghggf/article/details/86571505
目前,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,并不是随随便便就能发生定时调用的。一个线程是否进入可警告等待状态可参见微软的说明SleepEx function (synchapi.h) - Win32 apps | Microsoft Learn
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:因此其使用与内核对象的使用一个样,这就是所谓的触类旁通吧),如果定时器发出报时信息,而你的线程正在等待之中,那么你的线程就会醒来。
定时器队列(Timer Queue)可以使用CreateTimerQueue函数创建。定时器队列中的定时器是轻量级对象,可以在一定时间间隔之后调用指定的回调函数(可以只调用一次,也可以是周期性的)。这种等待操作由线程池中某个线程处理的(系统级别)。
向定时器队列中添加定时器可以使用CreateTimerQueueTimer函数。更新一个计时器队列中的计时器可以使用 ChangeTimerQueueTimer 函数。这两个函数中你可以指定一个回调函数,如果设定时间到达,线程池中某个线程会调用该回调函数。
使用 DeleteTimerQueueTimer函数可以取消挂起的计时器。当不再使计时器队列的时候,调用 DeleteTimerQueueEx 函数删除计时器队列,该函数调用是会取消并删除任何在该队列中等待的计时器。
#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
using namespace std;
//事件对象
HANDLE g_Event = NULL;
static int COUNT = 0;
VOID CALLBACK TimerRoutine( PVOID lpParameter, BOOLEAN TimerOrWaitFired )
{
if(NULL == lpParameter)
{
OutputDebugString(_T("lpParameter is null。。"));
return;
}
printf("the values of param is :%d\n",*(int*)lpParameter);
COUNT++;
/*if(COUNT >= 3)
{
SetEvent(g_Event);
}*/
}
int main(int argc,TCHAR *argv[])
{
HANDLE hTimer= NULL;
HANDLE hTimerQueue = NULL;
int arg = 123;
g_Event = CreateEvent(NULL,TRUE,FALSE,NULL);
if(NULL == g_Event)
{
OutputDebugString(_T("CreateEvent失败。。"));
return 1;
}
hTimerQueue = CreateTimerQueue();
if(NULL == hTimerQueue)
{
OutputDebugString(_T("CreateTimerQueue失败。。"));
return 1;
}
if(!CreateTimerQueueTimer(&hTimer,hTimerQueue,WAITORTIMERCALLBACK(TimerRoutine),&arg,5000,1000,NULL))
{
OutputDebugString(_T("CreateTimerQueueTimer失败。。"));
return 1;
}
if(WaitForSingleObject(g_Event,INFINITE) !=WAIT_OBJECT_0)
{
OutputDebugString(_T("WaitForSingleObject失败。。"));
}
CloseHandle(g_Event);
if(!DeleteTimerQueue(hTimerQueue))
{
OutputDebugString(_T("DeleteTimerQueue失败。。"));
}
return 0;
}
————————————————
版权声明:本文为CSDN博主「李朝淋」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/oshirdey/article/details/22927153