Partager via


Appels de procédure asynchrone

Un appel de procédure asynchrone (APC) est une fonction qui s’exécute de manière asynchrone dans le contexte d’un thread particulier. Lorsqu’un APC est mis en file d’attente vers un thread, le système émet une interruption logicielle. La prochaine fois que le thread est planifié, il exécute la fonction APC. Un APC généré par le système est appelé d'en mode noyau. Un APC généré par une application est appelé d’APC en mode utilisateur. Un thread doit être dans un état d’alerte pour exécuter un APC en mode utilisateur.

Chaque thread a sa propre file d’attente APC. Une application met en file d’attente un APC vers un thread en appelant la fonction QueueUserAPC. Le thread appelant spécifie l’adresse d’une fonction APC dans l’appel à QueueUserAPC. La mise en file d’attente d’un APC est une demande pour que le thread appelle la fonction APC.

Lorsqu’un APC en mode utilisateur est mis en file d’attente, le thread vers lequel il est mis en file d’attente n’est pas dirigé pour appeler la fonction APC, sauf s’il est dans un état d’alerte. Un thread entre dans un état d’alerte lorsqu’il appelle leSleepEx, SignalObjectAndWait, MsgWaitForMultipleObjectsEx, WaitForMultipleObjectsExou WaitForSingleObjectEx. Si l’attente est satisfaite avant que l’APC soit mis en file d’attente, le thread n’est plus dans un état d’attente alertable afin que la fonction APC ne soit pas exécutée. Toutefois, l’APC est toujours mis en file d’attente, de sorte que la fonction APC est exécutée lorsque le thread appelle une autre fonction d’attente alertable.

Les ReadFileEx, SetWaitableTimer, SetWaitableTimerEx, et fonctions WriteFileEx sont implémentées à l’aide d’un APC comme mécanisme de rappel de notification d’achèvement.

Si vous utilisez un pool de threads , notez que les API ne fonctionnent pas aussi bien que d’autres mécanismes de signalisation, car le système contrôle la durée de vie des threads de pool de threads. Il est donc possible qu’un thread soit arrêté avant la remise de la notification. Au lieu d’utiliser un mécanisme de signalisation basé sur l’APC, tel que le paramètre pfnCompletionRoutine de SetWaitableTimer ou SetWaitableTimerEx, utilisez un objet waitable tel qu’un minuteur créé avec CreateThreadpoolTimer. Pour les E/S, utilisez un objet d’achèvement d’E/S créé avec CreateThreadpoolIo ou un hEventstructure OVERLAPPED où l’événement peut être passé à la fonction SetThreadpoolWait.

Synchronisation interne

Lorsqu’une demande d’E/S est émise, une structure est allouée pour représenter la demande. Cette structure est appelée paquet de requête d’E/S (IRP). Avec les E/S synchrones, le thread génère l’IRP, l’envoie à la pile d’appareils et attend que le protocole IRP se termine dans le noyau. Avec les E/S asynchrones, le thread génère l’IRP et l’envoie à la pile d’appareils. La pile peut terminer immédiatement l’IRP ou retourner un état en attente indiquant que la demande est en cours. Lorsque cela se produit, l’IRP est toujours associé au thread. Il est donc annulé si le thread se termine ou appelle une fonction telle que CancelIo. En attendant, le thread peut continuer à effectuer d’autres tâches pendant que la pile d’appareils continue de traiter l’IRP.

Il existe plusieurs façons pour le système d’indiquer que l’IRP est terminé :

  • Mettez à jour la structure superposée avec le résultat de l’opération afin que le thread puisse interroger pour déterminer si l’opération s’est terminée.
  • Signalez l’événement dans la structure superposée afin qu’un thread puisse se synchroniser sur l’événement et être réveillé lorsque l’opération se termine.
  • Mettez en file d’attente l’IRP vers l’APC en attente du thread afin que le thread exécute la routine APC lorsqu’il entre dans un état d’attente alertable et retourne à partir de l’opération d’attente avec un état indiquant qu’il a exécuté une ou plusieurs routines APC.
  • Mettez en file d’attente l’IRP vers un port d’achèvement d’E/S, où il sera exécuté par le thread suivant qui attend le port d’achèvement.

Les threads qui attendent sur un port d’achèvement d’E/S n’attendent pas dans un état d’alerte. Par conséquent, si ces threads émettent des IRPs qui sont définis pour se terminer en tant qu’API sur le thread, ces achèvements IPC ne se produisent pas en temps voulu ; elles se produisent uniquement si le thread récupère une demande à partir du port d’achèvement des E/S, puis qu’il se produit pour entrer une attente alertable.

à l’aide d’un minuteur pouvant être attendu avec un appel de procédure asynchrone