PoEdu培训 Windows班 第三十八课 Windows 内核对象(五) 可等待计时器内核对象
文章类别: 培训笔记 0 评论

PoEdu培训 Windows班 第三十八课 Windows 内核对象(五) 可等待计时器内核对象

文章类别: 培训笔记 0 评论

Windows 内核对象(五) 可等待计时器内核对象

可等待计时器

有时我们可能需要在某些时间点上或者按照某些频率启动线程或者检测一些东西
这就需要用到我们的可等待计时器
可以使用CreateWaitableTimer API创建一个可等待计时器内核对象

CreateWaitableTimer

HANDLE WINAPI CreateWaitableTimer(
  _In_opt_ LPSECURITY_ATTRIBUTES lpTimerAttributes,
  _In_     BOOL                  bManualReset,
  _In_opt_ LPCTSTR               lpTimerName
);

lpTimerAttributes

bManualReset

lpTimerName

CreateWaitableTimer创建完成后, 它是无信号状态的
通过SetWaitableTimer来设置可等待计时器对象的信号状态

SetWaitableTimer

BOOL WINAPI SetWaitableTimer(
  _In_           HANDLE           hTimer,
  _In_     const LARGE_INTEGER    *pDueTime,
  _In_           LONG             lPeriod,
  _In_opt_       PTIMERAPCROUTINE pfnCompletionRoutine,
  _In_opt_       LPVOID           lpArgToCompletionRoutine,
  _In_           BOOL             fResume
);

hTimer

pDueTime

lPeriod

pfnCompletionRoutine

lpArgToCompletionRoutine

fResume

使用例子

#include <Windows.h>
#include <process.h>
#include <tchar.h>

INT _tmain()
{
    // 创建一个手动匿名的可等待计时器对象
    HANDLE hTime = CreateWaitableTimer(NULL, TRUE, NULL);
    if (WaitForSingleObject(hTime, 20) == WAIT_TIMEOUT)
        _tprintf(TEXT("Wait Timeout....\n"));
    // 通过代码我们可以得知, WaitableTimer创建完成后是无信号状态

    // 使用相对时间
    LARGE_INTEGER liDueTime;
    // 1s = 1000ms = 1000000微秒 = 1000000000纳秒 = 1000000000000皮秒 = 10 ^ 15飞秒 = 10 ^ 18啊秒 = 10 ^ 21仄秒 = 10 ^ 24幺秒
    // 这里设置 10秒, 也就是 10 000 000 000 纳秒
    // 注意, 这个参数的单位是 100ns, 所以设置 100 000 000
    // 使用相对时间, 最终是  -100 000 000
    liDueTime.QuadPart = -100000000LL;
    // 设置10秒后有信号
    SetWaitableTimer(hTime, &liDueTime, 1000, NULL, NULL, FALSE);

    if (WaitForSingleObject(hTime, INFINITE) == WAIT_OBJECT_0)
        _tprintf(TEXT("WaitableTimer is Signaled.....\n"));

    CloseHandle(hTime);

    return 0;
}

自动和手动状态

实验代码:

#include <Windows.h>
#include <process.h>
#include <tchar.h>

HANDLE g_hTimer = INVALID_HANDLE_VALUE;
CRITICAL_SECTION g_Cs;

UINT WINAPI ThreadFunc(LPVOID lParam)
{
    while (WaitForSingleObject(g_hTimer, INFINITE) == WAIT_OBJECT_0)
    {
        EnterCriticalSection(&g_Cs);
        _tprintf(TEXT("Thread [%d] is Runing.....\n"), (INT)lParam);
        // 手动模式下 1秒的定时触发周期不管用  
        // 必须再次调用SetWaitableTimer
        //LARGE_INTEGER liDueTime;
        //liDueTime.QuadPart = -10000000LL;
        //SetWaitableTimer(g_hTimer, &liDueTime, 1000, NULL, NULL, FALSE);
        LeaveCriticalSection(&g_Cs);
    }
    return 0;
}

INT _tmain()
{
    InitializeCriticalSection(&g_Cs);
    // 创建一个手动匿名的可等待计时器对象
    //g_hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
    // 创建一个自动匿名的可等待计时器对象
    g_hTimer = CreateWaitableTimer(NULL, FALSE, NULL);
    HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, (LPVOID)1, 0, NULL);
    // 注释下一行代码查看1条线程的效果
    HANDLE hThread2 = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, (LPVOID)2, 0, NULL);

    // 使用相对时间
    LARGE_INTEGER liDueTime;
    // 1s = 1000ms = 1000000微秒 = 1000000000纳秒 = 1000000000000皮秒 = 10 ^ 15飞秒 = 10 ^ 18啊秒 = 10 ^ 21仄秒 = 10 ^ 24幺秒
    // 这里设置 1秒, 也就是 1 000 000 000 纳秒
    // 注意, 这个参数的单位是 100ns, 所以设置 10 000 000
    // 使用相对时间, 最终是  -10 000 000
    liDueTime.QuadPart = -10000000LL;
    // 设置1秒后有信号 间隔1秒触发
    SetWaitableTimer(g_hTimer, &liDueTime, 1000, NULL, NULL, FALSE);

    WaitForSingleObject(hThread, INFINITE);
    DeleteCriticalSection(&g_Cs);

    CloseHandle(hThread);
    // 注释下一行代码查看1条线程的效果
    CloseHandle(hThread2);
    CloseHandle(g_hTimer);

    return 0;
}
在多条线程执行的时候, 我们发现线程1和线程2都可以进行执行   
这和我们之前所学的, 线程抢占式执行有些冲突啊?   
其实不是   
在Wait状态下, 线程是不可调度的, `不存在抢占式执行`   
我们的操作系统有一套算法   
这套算法会`尽可能的保证每个Wait能够得到信号`   
它会`尽可能的保持公平`   
包括我们的`事件内核对象`也是一样的    

注: 我实验的结果并不是这样的
在自动模式下, 线程一条一条的执行
在手动模式下, 线程同时可以执行
似乎和电脑配置有关系, 请见仁见智, 自主实验

APC回调

函数原型:

VOID CALLBACK TimerAPCProc(
  _In_opt_ LPVOID lpArgToCompletionRoutine,
  _In_     DWORD  dwTimerLowValue,
  _In_     DWORD  dwTimerHighValue
);

例子:

#include <Windows.h>
#include <process.h>
#include <tchar.h>

HANDLE g_hTime = INVALID_HANDLE_VALUE;

UINT WINAPI ThreadAPCTest(LPVOID lParam)
{
    while (WaitForSingleObject(g_hTime, INFINITE) == WAIT_OBJECT_0)
        _tprintf(TEXT("ThreadAPCTest is runing....\n"));
    return 0;
}

VOID CALLBACK TimerAPCProc(
    _In_opt_ LPVOID lpArgToCompletionRoutine,
    _In_     DWORD  dwTimerLowValue,
    _In_     DWORD  dwTimerHighValue
)
{
    while (WaitForSingleObject(g_hTime, INFINITE) == WAIT_OBJECT_0)
        _tprintf(TEXT("APC Callback is runing....\n"));
}

INT _tmain()
{
    g_hTime = CreateWaitableTimer(NULL, TRUE, NULL);
    //g_hTime = CreateWaitableTimer(NULL, FALSE, NULL);

    HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadAPCTest, NULL, 0, NULL);

    LARGE_INTEGER li = { 0 };
    // 将一个函数压入到调用SetWaitableTimer的线程的APC队列中
    // 设置立即有信号
    SetWaitableTimer(g_hTime, &li, 2000, TimerAPCProc, NULL, FALSE);
    // 触发APC
    SleepEx(INFINITE, TRUE);

    CloseHandle(hThread);
    CloseHandle(g_hTime);

    return 0;
}

未完待续...

如有错误,请提出指正!谢谢.

回复