您的位置:首页 > 其它

windows笔记-【内核对象线程同步】等待定时器内核对象

2013-07-17 11:48 162 查看
等待定时器是在某个时间或按规定的间隔时间发出自己的信号通知的内核对象。它们通常用来在某个时间执行某个操作。

创建等待定时器

HANDLE CreateWaitableTimer(
PSECURITY_ATTRIBUTES psa,
BOOL fManualReset,
PCTSTR pszName
);

psa和pszName这两个参数(见内核对象的安全性跨越进程边界共享内核对象【命名对象】

fManualReset参数用于指明人工重置的定时器或自动重置的定时器。(与事件的情况一样。见【内核对象线程同步】事件内核对象

人工重置的定时器信号通知时,等待该定时器的所有线程均变为可调度线程。

自动重置的定时器信号通知时,只有一个等待的线程变为可调度线程。

进程可以获得它自己的与进程相关的现有等待定时器的句柄,方法是调用OpenWaitableTimer函数: (见:跨越进程边界共享内核对象【命名对象】::按名字共享对象的另一种方法)

HANDLE OpenWaitableTimer(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName
);

等待定时器对象总是在未通知状态中创建(不像事件对象那样,可以让我们指定)。

SetWaitableTimer函数告诉定时器你想在何时让它成为已通知状态

BOOL SetWaitableTimer(
HANDLE hTimer,
const LARGE_INTEGER *pDueTime,
LONG lPeriod,
PTIMERAPCROUTINE pfnCompletionRoutine,
PVOID pvArgToCompletionRoutine,
BOOL fResume
);

hTimer参数用于指明你要设置的定时器。

pDueTime和lPeriod两个参数是一道使用的。PDueTimer参数用于指明定时器何时应该第一次报时,而lPeriod参数则用于指明此后定时器应该间隔多长时间报时一次。

例子

下面的代码用于将定时器的第一次报时的时间设置在2002年1月1日的下午1点钟,然后每隔6小时报时一次:

// Declare our local variables.
HANDLE hTimer;
SYSTEMTIME st;
FILETIME ftLocal, ftUTC;
LARGE_INTEGER liUTC;

// Create an auto-reset timer.
hTimer = CreateWaitableTimer(NULL, FALSE, NULL);

// First signaling is at January 1, 2002, at 1:00 P.M. (local time).
st.wYear = 2002; // Year
st.wMonth = 1; // January
st.wDayOfWeek = 0; // Ignored
st.wDay = 1; // The first of the month
st.wHour = 13; // 1PM
st.wMinute = 0; // 0 minutes into the hour
st.wSecond = 0; // 0 seconds into the minute
st.wMilliseconds = 0; // 0 milliseconds into the second

SystemTimeToFileTime(&st, &ftLocal);

// Convert local time to UTC time.
LocalFileTimeToFileTime(&ftLocal, &ftUTC);
// Convert FILETIME to LARGE_INTEGER because of different alignment.
liUTC.LowPart = ftUTC.dwLowDateTime;
liUTC.HighPart = ftUTC.dwHighDateTime;

// Set the timer.
SetWaitableTimer(hTimer, &liUTC, 6 * 60 * 60 * 1000, NULL, NULL, FALSE);
...

代码首先对SYSTEMTIME结构进行初始化,该结构用于指明定时器何时第一次报时(发出信号通知)。将该时间设置为本地时间,即计算机所在时区的正确时间。SetWaitableTimer的第二个参数的原型是个常量LARGE_INTEGER *,因此它不能直接接受SYSTEMTIME结构。但是,FILETIME结构和LARGE_INTEGER结构拥有相同的二进制格式,都包含两个32位的值。因此,我们可以将SYSTETIME结构转换成FILETIME结构。再一个问题是,SetWaitableTimer希望传递给它的时间始终都采用世界协调时(UTC)的时间。调用LocalFileTimeToFileTime函数,就可以很容易地进行时间的转换。

现在,若要使定时器在2002年1月1日下午1点之后每隔6 h进行一次报时,我们应该将注意力转向lPeriod参数。该参数用于指明定时器在初次报时后每隔多长时间(以毫秒为单位)进行一次报时。如果是每隔6h进行一次报时,那么我传递21600000(6 h×每小时60min×每分钟60s×每秒1000 ms)。另外,如果给它传递了以前的一个绝对时间,比如1 9 7 5年1月1日下午1点,那么SetWaitableTimer的运行就不会失败。

让定时器在一个相对于调用SetWaitableTimer的时间进行报时
代码下载


只需要在pDueTime参数中传递一个负值。传递的值必须是以100ns为间隔。由于我们通常并不以100ns的间隔来思考问题,因此我们要说明一下100ns的具体概念:1s = 1000ms = 1000000µs
= 100000000ns 。

g_hTimer = CreateWaitableTimer(NULL, FALSE, NULL);

constint nTimerUnitsPerSecond =10000000;
LARGE_INTEGER li;
li.QuadPart =-(3* nTimerUnitsPerSecond);
SetWaitableTimer(g_hTimer, &li, 1000, NULL, NULL, FALSE);


通常情况下,你可能想要一个一次报时的定时器,它只是发出一次报时信号,此后再也不发出报时信号。若要做到这一点,只需要为lPeriod参数传递0即可。然后可以调用CloseHandle函数,关闭定时器,或者再次调用SetWaitableTimer函数,重新设置时间,为它规定一个需要遵循的新条件。

参数是fResume,它可以用于支持暂停和恢复的计算机。通常可以为该参数传递FALSE,就像我在上面这个代码段中设置的那样。但是,如果你编写了一个会议安排类型的应用程序,在这个应用程序在中,你想设置一个为用户提醒会议时间安排的定时器,那么应该传递TRUE。当定时器报时的时候,它将使计算机摆脱暂停方式(如果它处于暂停状态的话),并唤醒等待定时器报时的线程。然后该应用程序运行一个波形文件,并显示一个消息框,告诉用户即将举行的会议。如果为fResume参数传递FALSE,定时器对象就变为已通知状态,但是它唤醒的线程必须等到计算机恢复运行(通常由用户将它唤醒)之后才能得到CPU时间。(还不是很明白,
暂停计算机是什么意思,进入睡眠吗?)

BOOL CancelWaitableTimer(HANDLE hTimer);

这个简单的函数用于取出定时器的句柄并将它撤消,这样,除非接着调用SetWaitableTi mer数以便重新设置定时器,否则定时器决不会进行报时。如果想要改变定时器的报时条件,不必在调用SetWaitableTimer函数之前调用CancelWaitableTimer函数。每次调用SetWaitableTier函数,都会在设置新的报时条件之前撤消定时器原来的报时条件。

本文地址:/article/5259753.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: