• Windows编程--线程和内核对象的同步-等待定时器内核对象


    等待定时器是在某个时间或按规定的间隔时间,发出自己的信号通知的内核对象。它们通常用来在某个时间执行某个操作。

    (程序可以定时地做一些工作,而不需要人参与进去。比如每天定时地升级病毒库,定时地下载电影,定时地更新游戏里的人物。要想实现这些功能,就可以使用定时器的API函数CreateWaitableTimer和SetWaitableTimer来实现了,这对API函数创建的时钟是比较精确的,可以达到100倍的10亿分之一秒。)

    若要创建等待定时器,只需要调用CreateWaitableTimer函数:

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

    与事件的情况一样, fManualReset参数用于指明人工重置的定时器或自动重置的定时器。

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

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

    psa和pszName这两个参数为内核对象的安全属性 和内核对象的命名。

    当然,进程可以获得它自己的与进程相关的现有等待定时器的句柄,方法是调用OpenWaitableTimer函数:

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

    等待定时器对象总是在未通知状态中创建。必须调用SetWaitableTimer函数来告诉定时器你想在何时让它成为已通知状态:

    (FangSH 注:一创建就是未通知状态,要用SetWaitableTimer来设置什么时候为通知状态)

    BOOL SetWaitableTimer(
    HANDLE hTimer,
    const LARGE_INTEGER *pDueTime, //第一次时间
    LONG lPeriod, //间隔
    PTIMERAPCROUTINE pfnCompletionRoutine,
    PVOID pvArgToCompletionRoutine,
    BOOL fResume);

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

    pDueTimelPeriod两个参数是一道使用的。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 January1, 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 UTCtime. 什么是UTC 时间??
    LocalFileTimeToFileTime(&ftLocal, &ftUTC);
    // Convert FILETIME toLARGE_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点之后每隔6h进行一次报时,我们应该将注意力转向l Period参数。该参数用于指明定时器在初次报时后每隔多长时间(以毫秒为单位)进行一次报时。如果是每隔6 h进行一次报时,那么我传递21 600 000(6 h×每小时60min×每分钟60s×每秒1000ms)。另外,如果给它传递了以前的一个绝对时间,比如1975年1月1日下午1点,那么SetWaitableTimer的运行就不会失败。

    如果不设置定时器应该第一次报时的绝对时间,也可以让定时器在一个相对于调用SetWaitableTimer的时间进行报时。只需要在pDueTime参数中传递一个负值(FangSH注: 相对时间)传递的值必须是以100ns为间隔。由于我们通常并不以100ns的间隔来思考问题,因此我们要说明一下100ns的具体概念:1 s = 1000ms = 10000 00µs=1000000000ns 。

    下面的代码用于将定时器设置为在调用SetWaitableTimer函数后5s第一次报时:

    // Declare our local variables.
    HANDLE hTimer;
    LARGE_INTEGER li;

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

    // Set the timer to go off 5seconds after calling SetWaitableTimer.
    // Timer unit is 100-nanoseconds.
    const int nTimerUnitsPerSecond =10000000;

    // Negate the time so thatSetWaitableTimer knows we
    // want relative time instead ofabsolute time.
    li.QuadPart = -(5 *nTimerUnitsPerSecond); //负值意味时间是相对的

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

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

    SetWaitableTimer的最后一个参数是fResume它可以用于支持暂停和恢复的计算机

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

    除了上面介绍的定时器函数外,最后还有一个CancelWaitableTimer函数:

    BOOLCancelWaitableTimer(HANDLE hTimer);

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

    FangSH  2011-01-02

  • 相关阅读:
    bzoj3997 [TJOI2015]组合数学
    bzoj1143 [CTSC2008]祭祀river
    bzoj 2208 [Jsoi2010]连通数
    bzoj3713 [PA2014]Iloczyn
    bzoj1054 [HAOI2008]移动玩具
    习题6-5 使用函数验证哥德巴赫猜想(20 分)
    习题6-4 使用函数输出指定范围内的Fibonacci数(20 分)
    习题6-3 使用函数输出指定范围内的完数(20 分)
    习题6-2 使用函数求特殊a串数列和(20 分)
    分类统计字符个数(15 分)
  • 原文地址:https://www.cnblogs.com/fangshenghui/p/1926756.html
Copyright © 2020-2023  润新知