Win32API 新しいスレッドプール タイマーオブジェクトのAPI

Windows Vistaで新たに追加されたスレッドプールのタイマーオブジェクトのAPIを使う。

タイマーオブジェクトは、指定した期限に達したときにコールバック関数を呼び出すために使用する。


まず、CreateThreadpoolTimer関数でスレッドプールタイマーオブジェクトを取得する

PTP_TIMER WINAPI CreateThreadpoolTimer(
  _In_         PTP_TIMER_CALLBACK pfnti, //コールバック関数
  _Inout_opt_  PVOID pv, //コールバック関数へ渡す引数
  _In_opt_     PTP_CALLBACK_ENVIRON pcbe //TP_CALLBACK_ENVIRON構造体へのポインタ
);



次に、SetThreadpoolTimer関数でスレッドプールタイマーオブジェクトをセットする。

VOID WINAPI SetThreadpoolTimer(
  _Inout_   PTP_TIMER pti, //スレッドプールタイマーオブジェクトのハンドル
  _In_opt_  PFILETIME pftDueTime, //タイマーが期限に達するまでの時間
  _In_      DWORD msPeriod, //コールバック関数が呼び出される間隔
  _In_opt_  DWORD msWindowLength //システムがタイマー呼び出しで遅延を起こす時間の最大値
);



このSetThreadpoolTimer関数で注意が必要なのは、第2引数の pftDueTime である。この引数では、SetThreadpoolTimer関数が呼び出されてから、最初にコールバック関数が呼び出されるまでの時間を指定する。指定方法には2種類あって、正の数かゼロを指定すると、1601年1月1日(UTC)からの絶対時間を示すことになる。負の数を指定すると、現在の時刻からの相対的な時間を示すことになる。私の感覚では、現在の時刻から相対的な時間を扱うほうが多いのではないかと思うので、負の数を指定するケースの方が多いのではないかと思う。また、100ナノ秒単位で指定するということにも注意が必要である。



最後に、CloseThreadpoolTimer関数でスレッドプールタイマーオブジェクトを解放する。

VOID WINAPI CloseThreadpoolTimer(
  _Inout_  PTP_TIMER pti //スレッドプールタイマーオブジェクト(CreateThreadpoolTimer関数の戻り値)
);





以下に、新しいスレッドプールによるスレッドプールタイマーを用いたサンプルを示す。
プログラム実行直後から3秒後に初めてコールバック関数が呼ばれるようにしている。コールバック関数は2秒間隔で呼び出されるようにしており、コールバック関数の中では現在の時刻をコンソールに表示している。10秒間経過すると、スレッドプールタイマーオブジェクトを解放し、プログラムは終了する。

#include <windows.h>
#include <stdio.h>

//現在の時刻をコンソールに表示するコールバック関数
VOID NTAPI TimerCallback(PTP_CALLBACK_INSTANCE Instance,
    PVOID Context,
    PTP_TIMER Timer)
{
    SYSTEMTIME st;

    GetLocalTime(&st);

    printf("%02d:%02d:%02d\n",
        st.wHour,
        st.wMinute,
        st.wSecond);
}

int main()
{
    //スレッドプールタイマーオブジェクトを生成
    PTP_TIMER lpTimer =
        CreateThreadpoolTimer(TimerCallback, NULL, NULL);


    //SetThreadpoolTimer関数の呼び出し後、3秒後にコールバック関数が呼ばれるようにする
    ULARGE_INTEGER ulRelativeStartTime;
    ulRelativeStartTime.QuadPart = (LONGLONG)(-30000000);//100nano秒単位で指定する
    
    FILETIME ftStartTime;
    ftStartTime.dwHighDateTime = ulRelativeStartTime.HighPart;
    ftStartTime.dwLowDateTime = ulRelativeStartTime.LowPart;

    //タイマーを登録し、一定間隔でコールバック関数が呼ばれるようにする
    //2秒間隔でコールバック関数が呼ばれるように指定している。
    SetThreadpoolTimer(lpTimer,
        &ftStartTime,
        2000,
        0);

    //10秒待機
    Sleep(10 * 1000);

    //スレッドプールタイマーオブジェクトを解放する
    CloseThreadpoolTimer(lpTimer);

    return 0;
}


実行結果

12:55:12
12:55:14
12:55:16
12:55:18





スレッドプールタイマーオブジェクトは、WaitForThreadpoolTimerCallbacks関数を用いることでコールバック関数が終了するのを待機することができる。

VOID WINAPI WaitForThreadpoolTimerCallbacks(
  _Inout_  PTP_TIMER pti, //スレッドプールタイマーオブジェクト
  _In_     BOOL fCancelPendingCallbacks //キューに入れられたコールバック関数をキャンセルするかしないか
);



以下のプログラムで、WaitForThreadpoolTimerCallbacks関数の動作を検証する。
SetThreadpoolTimerが呼び出されたあとすぐにコールバック関数が呼び出される。このコールバック関数は5秒間スリープする。その間、WaitForThreadpoolTimerCallbacks関数がコールバック関数の終了を待機する。5秒経過後コールバック関数が終了すると、WaitForThreadpoolTimerCallbacks関数から制御が戻りプログラムが終了する。

#include <windows.h>
#include <stdio.h>

VOID NTAPI TimerCallback(PTP_CALLBACK_INSTANCE Instance,
    PVOID Context,
    PTP_TIMER Timer)
{
    puts("コールバック関数 開始");
    Sleep(5000);
    puts("コールバック関数 終了");
}

int main()
{
    PTP_TIMER lpTimer =
        CreateThreadpoolTimer(TimerCallback, NULL, NULL);


    ULARGE_INTEGER ulRelativeStartTime;
    ulRelativeStartTime.QuadPart = (LONGLONG)(0);
    
    FILETIME ftStartTime;
    ftStartTime.dwHighDateTime = ulRelativeStartTime.HighPart;
    ftStartTime.dwLowDateTime = ulRelativeStartTime.LowPart;

    SetThreadpoolTimer(lpTimer,
        &ftStartTime,
        10*1000,
        0);

    puts("WaitForThreadpoolTimerCallbacks開始");

    WaitForThreadpoolTimerCallbacks(lpTimer, FALSE);

    puts("WaitForThreadpoolTimerCallbacks終了");

    CloseThreadpoolTimer(lpTimer);

    return 0;
}


実行結果

WaitForThreadpoolTimerCallbacks開始
コールバック関数 開始
コールバック関数 終了
WaitForThreadpoolTimerCallbacks終了



TimerCallback関数で5秒間スリープしている間、WaitForThreadpoolTimerCallbacks関数がTimerCallback関数の終了を待機しているのがわかる。TimerCallback関数終了後に、WaitForThreadpoolTimerCallbacks関数から制御が戻っている。








参考

CreateThreadpoolTimer: http://msdn.microsoft.com/ja-jp/library/windows/desktop/ms682466(v=vs.85).aspx

SetThreadpoolTimer: http://msdn.microsoft.com/ja-jp/library/windows/desktop/ms686271(v=vs.85).aspx

CloseThreadpoolTimer: http://msdn.microsoft.com/ja-jp/library/windows/desktop/ms682040(v=vs.85).aspx

WaitForThreadpoolTimerCallbacks: http://msdn.microsoft.com/ja-jp/library/windows/desktop/ms687042(v=vs.85).aspx