E/S synchrones et asynchrones

Consultez également les exemples d’applications liées aux E/S.

Il existe deux types de synchronisation des entrées/sorties (E/S) : les E/S synchrones et les E/S asynchrones. Les E/S asynchrones sont également appelées E/S superposées.

Dans les E/S sur fichier synchrone, un thread démarre une opération d’E/S et entre immédiatement dans un état d’attente jusqu’à ce que la fin de la demande d’E/S. Un thread qui effectue une E/S sur fichier asynchrone envoie une demande d’E/S au noyau en appelant une fonction appropriée. Si la demande est acceptée par le noyau, le thread appelant continue de traiter un autre travail jusqu’à ce que le noyau lui signale que l’opération d’E/S est terminée. Il interrompt ensuite son travail en cours et traite les données de l’opération d’E/S si nécessaire.

Les deux types de synchronisation sont illustrés dans la figure suivante.

E/S synchrones et asynchrones

Dans les cas où l’on s’attend à ce qu’une demande d’E/S prenne beaucoup de temps, comme l’actualisation ou la sauvegarde d’une grande base de données ou une liaison de communication lente, les E/S asynchrones constituent généralement un bon moyen d’optimiser le traitement. Toutefois, pour les opérations d’E/S relativement rapides, la surcharge liée au traitement des demandes d’E/S du noyau et des signaux du noyau peut réduire l’intérêt des E/S asynchrones, en particulier s’il est nécessaire d’effectuer de nombreuses opérations d’E/S rapides. Dans ce cas, les E/S synchrones seraient plus appropriées. Les mécanismes et les détails de la mise en œuvre de ces tâches varient en fonction du type de gestionnaire de périphérique utilisé et des besoins particuliers de l’application. En d’autres termes, il existe généralement plusieurs façons de résoudre le problème.

Considérations relatives aux E/S synchrones et asynchrones

Si un fichier ou un appareil est ouvert pour les E/S synchrones (autrement dit, FILE_FLAG_OVERLAPPED n’est pas spécifié), les appels suivants à des fonctions telles que WriteFile peuvent bloquer l’exécution du thread appelant jusqu’à la survenue de l’un des événements suivants :

  • L’opération d’E/S se termine (dans cet exemple, une écriture de données).
  • Une erreur d’E/S se produit. (Par exemple, le canal est fermé à partir de l’autre extrémité).
  • Une erreur a été commise dans l’appel lui-même (par exemple, un ou plusieurs paramètres ne sont pas valides).
  • Un autre thread du processus appelle la fonction CancelSynchronousIo à l’aide du handle du thread bloqué, ce qui met fin aux E/S pour ce thread, entraînant l’échec de l’opération d’E/S.
  • Le thread bloqué est arrêté par le système ; par exemple, le processus lui-même est arrêté ou un autre thread appelle la fonction TerminateThread à l’aide du handle du thread bloqué. (Cette solution est généralement considérée comme un dernier recours et comme une mauvaise conception de l’application).

Dans certains cas, ce délai peut être inacceptable pour la conception et l’objectif de l’application. Les concepteurs d’applications doivent donc envisager d’utiliser des E/S asynchrones avec des objets de synchronisation de thread appropriés tels que les ports de terminaison d’E/S. Pour en savoir plus sur la synchronisation de threads, consultez À propos de la synchronisation.

Un processus ouvre un fichier pour les E/S asynchrones dans son appel à CreateFile en spécifiant l’indicateur FILE_FLAG_OVERLAPPED dans le paramètre dwFlagsAndAttributes. Si FILE_FLAG_OVERLAPPED n’est pas spécifié, le fichier est ouvert pour les E/S synchrones. Lorsque le fichier a été ouvert pour les E/S asynchrones, un pointeur vers une structure OVERLAPPED est transmis dans l’appel à ReadFile et WriteFile. Lors de l’exécution d’E/S synchrones, cette structure n’est pas requise dans les appels à ReadFile et WriteFile.

Remarque

Si un fichier ou un appareil est ouvert pour des E/S asynchrones, les appels ultérieurs à des fonctions comme WriteFile utilisant ce handle renvoient généralement immédiatement, mais peuvent également se comporter de manière synchrone en ce qui concerne l’exécution bloquée. Pour en savoir plus, consultez Les E/S de disque asynchrones apparaissent comme étant synchrones sur Windows.

Bien que CreateFile soit la fonction la plus fréquemment utilisée pour ouvrir des fichiers, des volumes de disque, des canaux anonymes et d’autres appareils similaires, les opérations d’E/S peuvent également être effectuées à l’aide d’un handle typecast à partir d’autres objets système tels qu’un socket créé par les fonctions socket ou accept.

Les handles vers les objets annuaire sont obtenus en appelant la fonction CreateFile avec l’attribut FILE_FLAG_BACKUP_SEMANTICS. Les handles d’annuaire ne sont presque jamais utilisés : les applications de sauvegarde font partie des rares applications qui les utilisent habituellement.

Après l’ouverture de l’objet de fichier pour les E/S asynchrones, il faut créer, initialiser et transmettre correctement une structure OVERLAPPED dans chaque appel à des fonctions comme ReadFile et WriteFile. Gardez à l’esprit ce qui suit lors de l’utilisation de la structure OVERLAPPED dans les opérations de lecture et d’écriture asynchrones :

  • Ne pas désallouer ou modifier la structure OVERLAPPED ou le tampon de données avant la fin de toutes les opérations d’E/S asynchrones sur l’objet fichier
  • Si vous déclarez votre pointeur vers la structure OVERLAPPED en tant que variable locale, ne quittez pas la fonction locale avant la fin de toutes les opérations d’E/S asynchrones sur l’objet fichier Si on quitte la fonction local de manière prématurée, la structure OVERLAPPED ne sera plus à sa portée et sera inaccessible aux fonctions ReadFile ou WriteFile qu’elle rencontre en dehors de cette fonction.

Vous pouvez également créer un événement et placer le handle dans la structure OVERLAPPED ; les fonctions d’attente peuvent ensuite être utilisées pour attendre que l’opération d’E/S se termine en attendant le handle d’événement.

Comme indiqué précédemment, lorsque l’on travaille avec un handle asynchrone, les applications doivent faire preuve de prudence lorsqu’il s’agit de déterminer quand libérer les ressources associées à une opération d’E/S spécifique sur ce handle. Si le handle est désalloué prématurément, ReadFile ou WriteFile peut signaler de manière erronée que l’opération d’E/S est terminée. En outre, la fonction WriteFile renvoie parfois TRUE avec une valeur GetLastError de ERROR_SUCCESS, même si elle utilise un handle asynchrone (qui peut également renvoyer FALSE avec ERROR_IO_PENDING). Les programmeurs habitués à la conception d’E/S synchrones libèrent généralement des ressources du tampon de données à ce stade, car TRUE et ERROR_SUCCESS indiquent que l’opération est terminée. Toutefois, si les ports de terminaison des E/S sont utilisés avec ce handle asynchrone, un paquet d’achèvement est également envoyé même si l’opération d’E/S s’est achevée immédiatement. En d’autres termes, si l’application libère des ressources après que WriteFile renvoie TRUE avec ERROR_SUCCESS en plus de la routine de port de terminaison d’E/S, elle aura une double condition sans erreur. Dans cet exemple, la recommandation serait de permettre à la routine du port de terminaison d’être la seule responsable de toutes les opérations de libération de ces ressources.

Le système ne conserve pas le pointeur de fichier sur des handles asynchrones vers des fichiers et des appareils qui prennent en charge les pointeurs de fichiers (c’est-à-dire les appareils de recherche). Par conséquent, la position du fichier doit être transmise aux fonctions de lecture et d’écriture dans les membres de données de décalage associés de la structure OVERLAPPED. Pour en savoir plus, consultez WriteFile et ReadFile.

La position du pointeur de fichier pour un handle synchrone est conservée par le système, car les données sont lues ou écrites et peuvent également être mises à jour à l’aide de la fonction SetFilePointer ou SetFilePointerEx.

Une application peut également attendre sur le handle de fichier pour synchroniser l’achèvement d’une opération d’E/S, mais il faut alors faire preuve d’une extrême prudence. Chaque fois qu’une opération d’E/S est lancée, le système d’exploitation définit le handle de fichier à l’état non signalé. Chaque fois qu’une opération d’E/S est achevée, le système d’exploitation définit le handle de fichier à l’état signalé. Par conséquent, si une application démarre deux opérations d’E/S et attend sur le handle de fichier, il n’existe aucun moyen de déterminer quelle opération est terminée lorsque le handle est défini à l’état signalé. Si une application doit effectuer plusieurs opérations d’E/S asynchrones sur un seul fichier, elle doit attendre le handle d’événement dans la structure OVERLAPPED spécifique pour chaque opération d’E/S, plutôt que sur le handle de fichier commun.

Pour annuler toutes les opérations d’E/S asynchrones en attente, utilisez :

  • CancelIo : cette fonction annule uniquement les opérations émises par le thread appelant pour le handle de fichier spécifié.
  • CancelIoEx : cette fonction annule toutes les opérations émises par les threads pour le handle de fichier spécifié.

Utilisez CancelSynchronousIo pour annuler les opérations d’E/S synchrones en attente.

Les fonction ReadFileEx et WriteFileEx permettent à une application de spécifier une routine à exécuter (consultez FileIOCompletionRoutine) lorsque la demande d’E/S asynchrone est terminée.