非同期プロシージャ 呼び出しでの待機可能タイマーの使用
次の例では、 非同期プロシージャ コール (APC) 関数 (完了ルーチンとも呼ばれます) を、タイマーが設定されたときに 待機可能なタイマー に関連付けます。 完了ルーチンのアドレスは 、SetWaitableTimer 関数の 4 番目のパラメーターです。 5 番目のパラメーターは、完了ルーチンに引数を渡すために使用できる void ポインターです。
完了ルーチンは、 SetWaitableTimer を呼び出したのと同じスレッドによって実行されます。 完了ルーチンを実行するには、このスレッドが警告可能な状態である必要があります。 これを実現するには、アラート可能な関数である SleepEx 関数を呼び出します。
各スレッドには APC キューがあります。 警告可能な関数の 1 つが呼び出された時点でスレッドの APC キューにエントリがある場合、スレッドはスリープ状態になりません。 代わりに、エントリが APC キューから削除され、完了ルーチンが呼び出されます。
APC キューにエントリが存在しない場合、待機が満たされるまでスレッドは中断されます。 APC キューにエントリを追加するか、タイムアウトによって、またはハンドルがシグナル化することで、待機を満たすことができます。 APC キュー内のエントリによって待機が満たされると、スレッドが起動し、完了ルーチンが呼び出されます。 この場合、関数の戻り値は WAIT_IO_COMPLETION。
完了ルーチンが実行されると、処理する APC キュー内の別の項目がチェックされます。 alertable 関数は、すべての APC エントリが処理された後にのみを返します。 したがって、エントリが処理できるよりも速く APC キューに追加されている場合は、アラート可能な関数の呼び出しが返されない可能性があります。 これは、完了ルーチンの実行に必要な時間よりも期間が短い場合に、待機可能タイマーで特に可能です。
APC で待機可能タイマーを使用している場合、タイマーを設定するスレッドは、タイマーのハンドルで待機しないようにする必要があります。 これにより、APC キューにエントリが追加された結果ではなく、タイマーがシグナル通知された結果としてスレッドがウェイクアップします。 その結果、スレッドはアラート可能な状態でなくなり、完了ルーチンは呼び出されません。 次のコードでは、タイマーがシグナル状態に設定された後にスレッドの APC キューにエントリが追加されると、 SleepEx の呼び出しによってスレッドが起動されます。
#define UNICODE 1
#define _UNICODE 1
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#define _SECOND 10000000
typedef struct _MYDATA {
LPCTSTR szText;
DWORD dwValue;
} MYDATA;
VOID CALLBACK TimerAPCProc(
LPVOID lpArg, // Data value
DWORD dwTimerLowValue, // Timer low value
DWORD dwTimerHighValue ) // Timer high value
{
// Formal parameters not used in this example.
UNREFERENCED_PARAMETER(dwTimerLowValue);
UNREFERENCED_PARAMETER(dwTimerHighValue);
MYDATA *pMyData = (MYDATA *)lpArg;
_tprintf( TEXT("Message: %s\nValue: %d\n\n"), pMyData->szText,
pMyData->dwValue );
MessageBeep(0);
}
int main( void )
{
HANDLE hTimer;
BOOL bSuccess;
__int64 qwDueTime;
LARGE_INTEGER liDueTime;
MYDATA MyData;
MyData.szText = TEXT("This is my data");
MyData.dwValue = 100;
hTimer = CreateWaitableTimer(
NULL, // Default security attributes
FALSE, // Create auto-reset timer
TEXT("MyTimer")); // Name of waitable timer
if (hTimer != NULL)
{
__try
{
// Create an integer that will be used to signal the timer
// 5 seconds from now.
qwDueTime = -5 * _SECOND;
// Copy the relative time into a LARGE_INTEGER.
liDueTime.LowPart = (DWORD) ( qwDueTime & 0xFFFFFFFF );
liDueTime.HighPart = (LONG) ( qwDueTime >> 32 );
bSuccess = SetWaitableTimer(
hTimer, // Handle to the timer object
&liDueTime, // When timer will become signaled
2000, // Periodic timer interval of 2 seconds
TimerAPCProc, // Completion routine
&MyData, // Argument to the completion routine
FALSE ); // Do not restore a suspended system
if ( bSuccess )
{
for ( ; MyData.dwValue < 1000; MyData.dwValue += 100 )
{
SleepEx(
INFINITE, // Wait forever
TRUE ); // Put thread in an alertable state
}
}
else
{
printf("SetWaitableTimer failed with error %d\n", GetLastError());
}
}
__finally
{
CloseHandle( hTimer );
}
}
else
{
printf("CreateWaitableTimer failed with error %d\n", GetLastError());
}
return 0;
}
関連トピック