Share via


Chiamate di procedure asincrone

Una chiamata di routine asincrona (APC) è una funzione che viene eseguita in modo asincrono nel contesto di un determinato thread. Quando un APC viene accodato a un thread, il sistema genera un interrupt software. Alla successiva pianificazione del thread, eseguirà la funzione APC. Un APC generato dal sistema è denominato APC in modalità kernel. Un APC generato da un'applicazione è denominato APC in modalità utente. Per eseguire un APC in modalità utente, un thread deve trovarsi in uno stato di avviso.

Ogni thread ha una propria coda APC. Un'applicazione accoda un oggetto APC a un thread chiamando la funzione QueueUserAPC . Il thread chiamante specifica l'indirizzo di una funzione APC nella chiamata a QueueUserAPC. L'accodamento di un APC è una richiesta che il thread chiami la funzione APC.

Quando un APC in modalità utente viene accodato, il thread a cui viene accodato non viene indirizzato per chiamare la funzione APC a meno che non si trova in uno stato di avviso. Un thread entra in uno stato di avviso quando chiama la funzione SleepEx, SignalObjectAndWait, MsgWaitForMultipleObjectsEx, WaitForMultipleObjectsEx o WaitForSingleObjectEx . Se l'attesa viene soddisfatta prima che il servizio APC venga accodato, il thread non è più in uno stato di attesa avvisabile in modo che la funzione APC non venga eseguita. Tuttavia, il servizio APC è ancora in coda, quindi la funzione APC verrà eseguita quando il thread chiama un'altra funzione di attesa di avviso.

Le funzioni ReadFileEx, SetWaitableTimer, SetWaitableTimerEx e WriteFileEx vengono implementate usando un APC come meccanismo di callback di notifica di completamento.

Se si usa un pool di thread, si noti che le API non funzionano e altri meccanismi di segnalazione perché il sistema controlla la durata dei thread del pool di thread, quindi è possibile che un thread venga terminato prima che venga recapitata la notifica. Anziché usare un meccanismo di segnalazione basato su APC, ad esempio il parametro pfnCompletionRoutine di SetWaitableTimer o SetWaitableTimerEx, usare un oggetto waitable, ad esempio un timer creato con CreateThreadpoolTimer. Per I/O, usare un oggetto di completamento I/O creato con CreateThreadpoolIo o una struttura OVERLAPPED basata su hEvent in cui l'evento può essere passato alla funzione SetThreadpoolWait.

Internals sincronizzazione

Quando viene eseguita una richiesta di I/O, viene allocata una struttura per rappresentare la richiesta. Questa struttura è denominata pacchetto di richiesta I/O (IRP). Con l'I/O sincrono, il thread compila l'IRP, lo invia allo stack di dispositivi e attende il completamento dell'IRP nel kernel. Con l'I/O asincrono, il thread compila l'IRP e lo invia allo stack di dispositivi. Lo stack potrebbe completare immediatamente l'IRP oppure potrebbe restituire uno stato in sospeso che indica che la richiesta è in corso. In questo caso, l'IRP è ancora associato al thread, quindi verrà annullato se il thread termina o chiama una funzione come CancelIo. Nel frattempo, il thread può continuare a eseguire altre attività mentre lo stack di dispositivi continua a elaborare l'IRP.

Esistono diversi modi in cui il sistema può indicare che l'IRP è stato completato:

  • Aggiornare la struttura sovrapposta con il risultato dell'operazione in modo che il thread possa eseguire il polling per determinare se l'operazione è stata completata.
  • Segnalare l'evento nella struttura sovrapposta in modo che un thread possa eseguire la sincronizzazione sull'evento e essere riattivato al termine dell'operazione.
  • Accodare l'IRP all'APC in sospeso del thread in modo che il thread eseguo la routine APC quando entra in uno stato di attesa di avviso e restituisca dall'operazione di attesa con uno stato che indica che ha eseguito una o più routine APC.
  • Accodare l'IRP a una porta di completamento di I/O, in cui verrà eseguita dal thread successivo che attende la porta di completamento.

I thread che attendono una porta di completamento di I/O non attendono uno stato di avviso. Pertanto, se tali thread generano irP impostati per il completamento come APC al thread, tali completamenti IPC non verranno eseguiti in modo tempestivo; si verificheranno solo se il thread preleva una richiesta dalla porta di completamento di I/O e quindi immette un'attesa di avviso.

Uso di un timer waitable con una chiamata di routine asincrona