Uso dei timer attivabili con una chiamata di procedura asincrona
Nell'esempio seguente, una funzione di chiamata di procedura asincrona (APC), nota anche come routine di completamento, viene associata a un timer attendibile quando quest'ultimo è impostato. L'indirizzo della routine di completamento è il quarto parametro della funzione SetWaitableTimer. Il quinto parametro è un puntatore void che è possibile usare per passare argomenti alla routine di completamento.
La routine di completamento verrà eseguita dallo stesso thread che ha chiamato SetWaitableTimer. Questo thread deve trovarsi in uno stato allertabile per eseguire la routine di completamento. A tale scopo, chiama la funzione SleepEx, che è una funzione segnalabile.
Ogni thread ha una coda APC. Se è presente una voce nella coda APC del thread al momento della chiamata a una delle funzioni di avviso, il thread non viene messo in sospensione. La voce viene invece rimossa dalla coda APC e viene chiamata la routine di completamento.
Se non esiste alcuna voce nella coda APC, il thread viene sospeso fino a quando l'attesa non sarà soddisfatta. L'attesa può essere soddisfatta aggiungendo una voce alla coda APC, da un timeout o quando un handle viene segnalato. Quando una voce nella coda APC soddisfa l'attesa, il thread viene risvegliato e la routine di completamento viene chiamata. In questo caso, il valore restituito della funzione è WAIT_IO_COMPLETION.
Dopo l'esecuzione della routine di completamento, il sistema verifica la presenza di un'altra voce nella coda APC da elaborare. Una funzione di avviso restituirà solo dopo l'elaborazione di tutte le voci APC. Pertanto, se le voci vengono aggiunte alla coda APC più velocemente di quanto possano essere elaborate, è possibile che una chiamata a una funzione avvisabile non venga mai restituita. Ciò è particolarmente possibile con i timer in attesa, se il periodo è più breve del tempo necessario per eseguire la routine di completamento.
Quando si utilizza un timer waitable con un APC, il thread che imposta il timer non dovrebbe attendere sull'handle del timer. In questo modo, il thread viene riattivato in seguito alla segnalazione del timer anziché come risultato dell'aggiunta di una voce alla coda APC. Di conseguenza, il thread non è più in uno stato allertabile e la routine di completamento non viene chiamata. Nel codice seguente, la chiamata a SleepEx risveglia il thread quando viene aggiunta una voce alla coda APC del thread dopo che il timer è impostato sullo stato segnalato.
#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;
}
Argomenti correlati