Freigeben über


Synchrone und asynchrone E/A

Siehe auch E/A-bezogene Beispielanwendungen.

Es gibt zwei Arten der Ein-/Ausgabesynchronisation (E/A): synchrone E/A und asynchrone E/A. Asynchrone E/A wird auch als überlappende E/A bezeichnet.

Bei der synchronen Datei-E/A startet ein Thread einen E/A-Vorgang und geht sofort in einen Wartezustand über, bis die E/A-Anforderung abgeschlossen ist. Ein Thread, der asynchrone Datei-E/A durchführt, sendet eine E/A-Anforderung an den Kernel, indem er eine entsprechende Funktion aufruft. Wenn die Anforderung vom Kernel akzeptiert wird, fährt der aufrufende Thread mit der Bearbeitung eines anderen Auftrags fort, bis der Kernel dem Thread signalisiert, dass die E/A-Operation abgeschlossen ist. Er unterbricht dann seinen aktuellen Auftrag und verarbeitet die Daten aus der E/A-Operation wie erforderlich.

Die beiden Synchronisationsarten sind in der folgenden Abbildung dargestellt.

Screenshot eines Diagramms, das synchrone und asynchrone i/o veranschaulicht.

In Situationen, in denen eine E/A-Anforderung voraussichtlich viel Zeit in Anspruch nehmen wird, z. B. bei der Aktualisierung oder Sicherung einer großen Datenbank oder bei einer langsamen Kommunikationsverbindung, ist die asynchrone E/A im Allgemeinen eine gute Möglichkeit, die Verarbeitungseffizienz zu optimieren. Bei relativ schnellen E/A-Operationen kann der Overhead der Verarbeitung von Kernel-E/A-Anforderungen und Kernel-Signalen jedoch dazu führen, dass die asynchrone E/A weniger vorteilhaft ist, insbesondere wenn viele schnelle E/A-Operationen durchgeführt werden müssen. In diesem Fall wäre eine synchrone E/A besser. Die Mechanismen und Implementierungsdetails zur Bewältigung dieser Aufgaben hängen von der Art des verwendeten Gerätehandles und den besonderen Anforderungen der Anwendung ab. Mit anderen Worten: Es gibt in der Regel mehrere Möglichkeiten, das Problem zu lösen.

Überlegungen zu synchronen und asynchronen E/A

Wenn eine Datei oder ein Gerät für synchrone E/A geöffnet wird (d. h. FILE_FLAG_OVERLAPPED nicht angegeben ist), können nachfolgende Aufrufe von Funktionen wie WriteFile die Ausführung des aufrufenden Threads blockieren, bis eines der folgenden Ereignisse eintritt:

  • Die E/A-Operation ist abgeschlossen (in diesem Beispiel ein Schreiben von Daten).
  • Ein E/A-Fehler tritt auf. (Beispielsweise wird die Pipe vom anderen Ende her verschlossen.)
  • Bei der Anfrage selbst ist ein Fehler aufgetreten (z. B. ein oder mehrere Parameter sind ungültig).
  • Ein anderer Thread im Prozess ruft die CancelSynchronousIo-Funktion mithilfe des Thread-Handles des blockierten Threads auf, um die E/A für diesen Thread zu beenden, wodurch der E/A-Vorgang fehlschlägt.
  • Der blockierte Thread wird vom System beendet; Beispielsweise wird der Prozess selbst beendet, oder ein anderer Thread ruft die TerminateThread-Funktion mithilfe des Handles des blockierten Threads auf. (Dies wird im Allgemeinen als letzter Ausweg und nicht als gutes Anwendungsdesign betrachtet).

In einigen Fällen kann diese Verzögerung für das Design und den Zweck der Anwendung inakzeptabel sein. Daher sollten Anwendungsentwickler die Verwendung asynchroner E/A mit geeigneten Thread-Synchronisationsobjekten wie E/A-Abschlussports in Betracht ziehen. Weitere Informationen über die Synchronisierung von Threads finden Sie unter Über Synchronisierung.

Ein Prozess öffnet eine Datei für asynchrone E/A bei seinem Aufruf von CreateFile, indem das FILE_FLAG_OVERLAPPED-Flag im Parameter dwFlagsAndAttributes angegeben wird. Wenn FILE_FLAG_OVERLAPPED nicht angegeben ist, wird die Datei für synchrone E/A geöffnet. Wenn die Datei für asynchrone E/A geöffnet wurde, wird ein Zeiger auf eine OVERLAPPED-Struktur an den Aufruf von ReadFile und WriteFile übergeben. Bei synchroner E/A wird diese Struktur in Anfragen an ReadFile und WriteFile nicht benötigt.

Hinweis

Wenn eine Datei oder ein Gerät für asynchrone E/A geöffnet wird, kehren nachfolgende Aufrufe von Funktionen wie WriteFile mit diesem Handle im Allgemeinen sofort zurück, können sich jedoch auch synchron verhalten, wenn die Ausführung blockiert ist. Weitere Informationen finden Sie unter Asynchrone Festplatten-E/A erscheint unter Windows als synchron.

Obwohl CreateFile die am häufigsten verwendete Funktion zum Öffnen von Dateien, Datenträgervolumes, anonymen Rohren und anderen ähnlichen Geräten ist, können E/A-Vorgänge auch mithilfe eines Handle Typecasts von anderen Systemobjekten wie einem Socket ausgeführt werden, der vom Socket erstellt oder Funktionen akzeptiert wird.

Handles für Verzeichnisobjekte werden durch das Aufrufen der Funktion CreateFile mit dem Attribut FILE_FLAG_BACKUP_SEMANTICS erlangt. Verzeichnis-Handles werden fast nie verwendet – Backup-Anwendungen sind eine der wenigen Anwendungen, die sie in der Regel verwenden.

Nach dem Öffnen des Dateiobjekts für asynchrone E/A muss eine OVERLAPPED-Struktur ordnungsgemäß erstellt, initialisiert und an jeden Aufruf an Funktionen wie ReadFile und WriteFile übergeben werden. Beachten Sie folgendes, wenn Sie die ÜBERLAPPUNG-Struktur in asynchronen Lese- und Schreibvorgängen verwenden:

  • Deallokieren oder ändern Sie die OVERLAPPED-Struktur oder den Datenpuffer nicht, bevor alle asynchronen E/A-Vorgänge zum Dateiobjekt abgeschlossen sind.
  • Wenn Sie den Zeiger auf die OVERLAPPED-Struktur als lokale Variable deklarieren, beenden Sie die lokale Funktion erst, wenn alle asynchronen E/A-Vorgänge für das Dateiobjekt abgeschlossen wurden. Wenn die lokale Funktion vorzeitig beendet wird, gerät die OVERLAPPED-Struktur außer Gültigkeitsbereich und ist für ReadFile- oder WriteFile-Funktionen, die sich außerhalb dieser Funktion befinden, nicht mehr zugänglich.

Sie können auch ein Ereignis erstellen und das Handle in der OVERLAPPED-Struktur platzieren. Die Wartefunktionen können dann verwendet werden, um einen E/A-Vorgang zu überwachen, indem auf das Ereignishandle gewartet wird.

Wie bereits erwähnt, sollten Anwendungen bei der Arbeit mit einem asynchronen Handle vorsichtig sein, wenn sie entscheiden, wann die mit einer bestimmten E/A-Operation an diesem Handle verbundenen Ressourcen freigegeben werden sollen. Wenn das Handle vorzeitig freigegeben wird, meldet ReadFile oder WriteFile möglicherweise fälschlicherweise, dass der E/A-Vorgang abgeschlossen ist. Darüber hinaus gibt die WriteFile-Funktion manchmal TRUE mit einem GetLastError-Wert von ERROR_SUCCESS zurück, obwohl ein asynchrones Handle verwendet wird (was auch FALSE mit ERROR_IO_PENDING zurückgeben kann). Programmierer, die an das synchrone E/A-Design gewöhnt sind, geben an dieser Stelle in der Regel die Datenpufferressourcen frei, da TRUE und ERROR_SUCCESS bedeuten, dass die Operation abgeschlossen ist. Wenn jedoch E/A-Abschlussports mit diesem asynchronen Handle verwendet werden, wird auch ein Abschlusspaket gesendet, obwohl der E/A-Vorgang sofort abgeschlossen wurde. Mit anderen Worten, wenn die Anwendung Ressourcen freigibt, nachdem WriteFile mit ERROR_SUCCESSTRUE zurückgegeben hat, und zwar zusätzlich zur I/O Completion Port Routine, hat sie eine doppelt-freie Fehlerbedingung. In diesem Beispiel würde die Empfehlung lauten, der Completion Port Routine die alleinige Verantwortung für alle Freigabeoperationen für solche Ressourcen zu übertragen.

Das System verwaltet den Dateizeiger nicht auf asynchronen Handles für Dateien und Geräte, die Dateizeiger unterstützen (d. h. nach Geräten suchen), daher muss die Dateiposition an die Lese- und Schreibfunktionen in den zugehörigen Offsetdatenelementen der OVERLAPPED-Struktur übergeben werden. Weitere Informationen finden Sie unter WriteFile und ReadFile.

Die Position des Dateizeigers für ein synchrones Handle wird vom System verwaltet, während Daten gelesen oder geschrieben werden, und kann auch mithilfe der SetFilePointer- oder SetFilePointerEx-Funktion aktualisiert werden.

Eine Anwendung kann auch auf das Dateihandle warten, um den Abschluss einer E/A-Operation zu synchronisieren, aber dabei ist äußerste Vorsicht geboten. Jedes Mal, wenn eine E/A-Operation gestartet wird, setzt das Betriebssystem das Dateihandle in den nicht signalisierten Zustand. Jedes Mal, wenn ein E/A-Vorgang abgeschlossen ist, setzt das Betriebssystem das Dateihandle in den signalisierten Zustand. Wenn eine Anwendung also zwei E/A-Operationen startet und auf das Dateihandle wartet, gibt es keine Möglichkeit festzustellen, welche Operation beendet ist, wenn das Handle in den signalisierten Zustand versetzt wird. Wenn eine Anwendung mehrere asynchrone E/A-Vorgänge für eine einzelne Datei ausführen muss, sollte sie auf das Ereignishandle in der spezifischen ÜBERLAPPUNGS-Struktur für jeden E/A-Vorgang warten, anstatt auf dem allgemeinen Dateihandle.

Abbrechen von E/A-Vorgängen

Um alle anstehenden asynchronen E/A-Operationen abzubrechen, verwenden Sie entweder:

  • CancelIo: Diese Funktion bricht nur Vorgänge ab, die vom aufrufenden Thread für das angegebene Dateihandle ausgegeben werden.
  • CancelIoEx: Diese Funktion bricht alle Vorgänge ab, die von den Threads für das angegebene Dateihandle ausgegeben werden.

Verwenden Sie CancelSynchronousIo , um ausstehende synchrone E/A-Vorgänge abzubrechen.

Mit den Funktionen ReadFileEx und WriteFileEx kann eine Anwendung eine Auszuführende Routine angeben (siehe FileIOCompletionRoutine), wenn die asynchrone E/A-Anforderung abgeschlossen ist.

WriteFile-

ReadFile-