内核对象--可等待计时器WaitableTimer(二)

来源:互联网 发布:数控车床左牙怎么编程 编辑:程序博客网 时间:2024/05/21 05:37

上节,介绍了时间一到就触发计时器对象;

这节,主要介绍时间一到触发计时器对象,同时计时器向线程的APC队列添加一个APC函数。当计时器被触发的时候,如果线程处于可提醒状态,系统会让线程调用回调函数。


为了添加一个APC到队列中,需要按如下步骤操作:

1、实现一个APC函数

VOID    APIENTRY    TimerAPCToutine(

    PVOID  pvArgToCompletionRoutine,  //SetWaitableTimer制定的值,传递一些上下文信息

    DWORD  dwTimeLowValue, DWORD  dwTimeHighValue //两个值表示计时器被触发的时间

);

2、设置SetWaitableTimer的两个参数值pfnCompletionRoutine和pvArgToCompletionRoutine

pfnCompletionRoutine: 设置APC函数地址,用以将该函数加入到APC队列

pvArgToCompletionRoutine: 跟APC函数的一致

3、SetWaitableTimer调用者线程必须处于可提醒状态,且该APC函数应该被同一线程(SetWaitableTimer调用者线程)调用

线程必须由如下函数进入等待状态:SleepEx, WaitForSingleObjectEx, WaitForMultipleObjectsEx, MsgWaitForMultipleObjectsEx, SignalObjectAndWait

4、确保APC函数会在计时器的下一次被触发之前执行结束

为什么要强调这一点呢?因为,只有当所有的APC函数都处理完毕后,才会返回到等待函数(WaiForSingleObjectEx等)。因此,必须确保自己的APC函数会在计时器再次触发前执行完毕,否则APC函数的加入队列的速度大于APC函数自身的执行速度。

// WaitableTimer.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include <Windows.h>#include <atlstr.h>HANDLEg_hTimer = NULL;HANDLEg_hThread= NULL;DWORDg_dwThreadID_1 = 0;DWORDg_dwThreadID_2 = 0;SYSTEMTIMEg_stime;DWORDThreadHander(LPVOID lpThreadParameter);VOIDTimerAPCRoutine(PVOID pvArgToCompletionRoutine, DWORD dwTimerLowValue, DWORD dwTimerHighValue);int _tmain(int argc, _TCHAR* argv[]){#if 0//Create Threadg_hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadHander, &g_dwThreadID_1, 0, &g_dwThreadID_1);if(g_hThread != NULL) {printf("CreateThread() is succesful!\n");printf("Thread:%p, ThreadID:%d\n", g_hThread, g_dwThreadID_1);}//CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadHander, &g_dwThreadID_2, 0, &g_dwThreadID_2);//启动线程//ResumeThread(g_hThread);#endif//Create Waitable Timerg_hTimer = CreateWaitableTimer(NULL, FALSE, NULL);if(g_hTimer != NULL) {printf("CreateWaitableTimer() call is successful!\n");if (ERROR_ALREADY_EXISTS == GetLastError()) {printf("WaitableTimer have Already exists!\n");}}//设置定时器2014/1/25 21:02:00启动时间, 启动以后再间隔5秒启动一次SYSTEMTIMEst;FILETIMEftLocal, ftUTC;LARGE_INTEGERliUTC = {0};st.wYear = 2014;st.wMonth= 1;st.wDayOfWeek = 0;st.wDay = 25;st.wHour= 21;st.wMinute = 16;st.wSecond = 0;st.wMilliseconds = 0;SystemTimeToFileTime(&st, &ftLocal);//转换本地时间到UTC时间LocalFileTimeToFileTime(&ftLocal, &ftUTC);//转换FILETIME到LARGE_INTEGER//liUTC.LowPart = ftUTC.dwLowDateTime;//liUTC.HighPart= ftUTC.dwHighDateTime;//liUTC.QuadPart= -5*10000000; //设置函数调用后5秒钟开始执行GetLocalTime(&g_stime);printf("开始时间:%02d:%02d:%02d %03d\n", g_stime.wHour, g_stime.wMinute, g_stime.wSecond, g_stime.wMilliseconds);if (FALSE != SetWaitableTimer(g_hTimer,&liUTC, 10*1000, /*NULL*/(PTIMERAPCROUTINE)&TimerAPCRoutine, "Hello", TRUE) ) {printf("SetWaitableTimer() return TRUE!\n");}ThreadHander(&g_dwThreadID_1);int n=1;while(true) {Sleep(1000);//printf("main() %d\n", n++);}system("pause");return 0;}DWORD ThreadHander(LPVOID lpThreadParameter){int n=1;SYSTEMTIMEst;while(true) {switch(WaitForSingleObjectEx(g_hTimer, INFINITE, TRUE)){caseWAIT_OBJECT_0:GetLocalTime(&st);printf(">> 时间:线程%d, %02d:%02d:%02d %03d\n", *((int*)lpThreadParameter), st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);break;caseWAIT_TIMEOUT:printf("WaitForSingleObject(): WAIT_TIMEOUT\n");break;caseWAIT_ABANDONED:printf("WaitForSingleObject(): WAIT_ABANDONED\n");break;caseWAIT_FAILED:printf("WaitForSingleObject(): WAIT_FAILED,错误%d\n", GetLastError());break;}}printf("线程退出:%d\n", *((int*)lpThreadParameter));return0;}VOIDTimerAPCRoutine(PVOID pvArgToCompletionRoutine, DWORD dwTimerLowValue, DWORD dwTimerHighValue){TCHARszBuf[512]={0};SYSTEMTIMELocal;FILETIMEftUTC;ftUTC.dwLowDateTime = dwTimerLowValue;ftUTC.dwHighDateTime= dwTimerHighValue;//转换UTC时间到本地时间FileTimeToSystemTime(&ftUTC, &Local);//格式化时间字符串GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &Local, NULL, szBuf, _countof(szBuf));_tcscat_s(szBuf, _countof(szBuf), TEXT(" "));GetTimeFormat(LOCALE_USER_DEFAULT, 0, &Local, NULL, _tcschr(szBuf, TEXT('\0')), (int)(_countof(szBuf) - _tcslen(szBuf)));printf("TimerAPCRoutine:%02d:%02d:%02d, pvArgToCompletionRoutine=%s\n", Local.wHour, Local.wMinute, Local.wSecond,(char*)pvArgToCompletionRoutine);}


上面的例子有一个问题,就是线程不应该在等待一个计时器句柄的同时以可提醒的方式等待同一个计时器。仔细看上面的图上箭头所指的一行与下面几行就会发现第一次是内核对象句柄,后面的才是可提醒的;

正确的做法,如下:

比较执行结果,就会发现这种方法比上面的要好。

DWORD ThreadHander(LPVOID lpThreadParameter){int n=1;SYSTEMTIMEst;while(true) {SleepEx(INFINITE, TRUE);GetLocalTime(&st);printf(">> 时间:线程%d, %02d:%02d:%02d %03d\n", *((int*)lpThreadParameter), st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);#if 0switch(WaitForSingleObjectEx(g_hTimer, INFINITE, FALSE)){caseWAIT_OBJECT_0:GetLocalTime(&st);printf(">> 时间:线程%d, %02d:%02d:%02d %03d\n", *((int*)lpThreadParameter), st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);break;caseWAIT_TIMEOUT:printf("WaitForSingleObject(): WAIT_TIMEOUT\n");break;caseWAIT_ABANDONED:printf("WaitForSingleObject(): WAIT_ABANDONED\n");break;caseWAIT_FAILED:printf("WaitForSingleObject(): WAIT_FAILED,错误%d\n", GetLastError());break;}#endif}printf("线程退出:%d\n", *((int*)lpThreadParameter));return0;}


0 0