Synchronisierung und überlappende Eingabe und Ausgabe

Sie können entweder synchrone oder asynchrone (auch als überlappende) E/A-Vorgänge für Dateien, Named Pipes und serielle Kommunikationsgeräte ausführen. Die Funktionen WriteFile, ReadFile, DeviceIoControl, WaitCommEvent, ConnectNamedPipe und TransactNamedPipe können entweder synchron oder asynchron ausgeführt werden. Die Funktionen ReadFileEx und WriteFileEx können nur asynchron ausgeführt werden.

Wenn eine Funktion synchron ausgeführt wird, wird sie erst zurückgegeben, wenn der Vorgang abgeschlossen wurde. Dies bedeutet, dass die Ausführung des aufrufenden Threads auf unbestimmte Zeit blockiert werden kann, während auf den Abschluss eines zeitaufwändigen Vorgangs gewartet wird. Funktionen, die für überlappende Vorgänge aufgerufen werden, können sofort zurückgegeben werden, auch wenn der Vorgang noch nicht abgeschlossen wurde. Dadurch kann ein zeitaufwändiger E/A-Vorgang im Hintergrund ausgeführt werden, während der aufrufende Thread andere Aufgaben ausführen kann. Beispielsweise kann ein einzelner Thread gleichzeitige E/A-Vorgänge für verschiedene Handles oder sogar gleichzeitige Lese- und Schreibvorgänge für dasselbe Handle ausführen.

Um die Ausführung mit dem Abschluss des überlappenden Vorgangs zu synchronisieren, verwendet der aufrufende Thread die GetOverlappedResult-Funktion , die GetOverlappedResultEx-Funktion oder eine der Wartefunktionen , um zu bestimmen, wann der überlappende Vorgang abgeschlossen wurde. Sie können auch das HasOverlappedIoCompleted-Makro verwenden, um den Abschluss abzufragen.

Um alle ausstehenden asynchronen E/A-Vorgänge abzubrechen, verwenden Sie die CancelIoEx-Funktion , und stellen Sie eine OVERLAPPED-Struktur bereit, die die Anforderung zum Abbrechen angibt. Verwenden Sie die CancelIo-Funktion , um ausstehende asynchrone E/A-Vorgänge abzubrechen, die vom aufrufenden Thread für das angegebene Dateihandle ausgegeben werden.

Überlappende Vorgänge erfordern eine Datei, eine Named Pipe oder ein Kommunikationsgerät, das mit dem flag FILE_FLAG_OVERLAPPED erstellt wurde. Wenn ein Thread eine Funktion (z. B. die ReadFile-Funktion ) aufruft, um einen überlappenden Vorgang auszuführen, muss der aufrufende Thread einen Zeiger auf eine OVERLAPPED-Struktur angeben. (Wenn dieser Zeiger NULL ist, gibt der Rückgabewert der Funktion möglicherweise fälschlicherweise an, dass der Vorgang abgeschlossen wurde.) Alle Member der OVERLAPPED-Struktur müssen auf Null initialisiert werden, es sei denn, ein Ereignis wird verwendet, um den Abschluss eines E/A-Vorgangs zu signalisieren. Wenn ein Ereignis verwendet wird, gibt der hEvent-Member der OVERLAPPED-Struktur ein Handle für das zugeordnete Ereignisobjekt an. Das System legt den Status des Ereignisobjekts auf nicht signalisiert fest, wenn ein Aufruf der E/A-Funktion zurückgibt, bevor der Vorgang abgeschlossen wurde. Das System legt den Status des Ereignisobjekts fest, der signalisiert wird, wenn der Vorgang abgeschlossen wurde. Ein Ereignis ist nur erforderlich, wenn mehrere ausstehende E/A-Vorgänge gleichzeitig vorhanden sind. Wenn kein Ereignis verwendet wird, signalisiert jeder abgeschlossene E/A-Vorgang die Datei, die Named Pipe oder das Kommunikationsgerät.

Wenn eine Funktion aufgerufen wird, um einen überlappenden Vorgang auszuführen, wird der Vorgang möglicherweise abgeschlossen, bevor die Funktion zurückgegeben wird. In diesem Fall werden die Ergebnisse so behandelt, als ob der Vorgang synchron ausgeführt worden wäre. Wenn der Vorgang jedoch nicht abgeschlossen wurde, ist der Rückgabewert der Funktion FALSE, und die GetLastError-Funktion gibt ERROR_IO_PENDING zurück.

Ein Thread kann überlappende Vorgänge mit einer der beiden Methoden verwalten:

  • Verwenden Sie die GetOverlappedResult- oder GetOverlappedResultEx-Funktion , um auf den Abschluss des überlappenden Vorgangs zu warten. Wenn GetOverlappedResultEx verwendet wird, kann der aufrufende Thread ein Timeout für den überlappenden Vorgang angeben oder einen warnbaren Wartevorgang ausführen.
  • Geben Sie ein Handle für das Ereignisobjekt manuell zurückgesetzt der OVERLAPPED-Struktur in einer der Wartefunktionen an, und rufen Sie dann getOverlappedResult oder GetOverlappedResultEx auf, nachdem die Wait-Funktion zurückgegeben wurde. Die Funktion gibt die Ergebnisse des abgeschlossenen überlappenden Vorgangs zurück, und für Funktionen, in denen solche Informationen geeignet sind, gibt sie die tatsächliche Anzahl der übertragenen Bytes an.

Wenn Sie mehrere gleichzeitige überlappende Vorgänge für einen einzelnen Thread ausführen, muss der aufrufende Thread eine OVERLAPPED-Struktur für jeden Vorgang angeben. Jede OVERLAPPED-Struktur muss ein Handle für ein anderes Ereignisobjekt mit manueller Zurücksetzung angeben. Um zu warten, bis einer der überlappenden Vorgänge abgeschlossen ist, gibt der Thread alle Manuell zurücksetzen-Ereignishandles als Wartekriterien in einer der Wartefunktionen mit mehreren Objekten an. Der Rückgabewert der Wartefunktion mit mehreren Objekten gibt an, welches Ereignisobjekt mit manueller Zurücksetzung signalisiert wurde, sodass der Thread bestimmen kann, welcher überlappende Vorgang den Wartevorgang abgeschlossen hat.

Es ist sicherer, für jeden überlappenden Vorgang ein separates Ereignisobjekt zu verwenden, anstatt kein Ereignisobjekt anzugeben oder dasselbe Ereignisobjekt für mehrere Vorgänge wiederzuverwenden. Wenn in der OVERLAPPED-Struktur kein Ereignisobjekt angegeben ist, signalisiert das System den Zustand der Datei, der Named Pipe oder des Kommunikationsgeräts, wenn der überlappende Vorgang abgeschlossen wurde. Daher können Sie diese Handles als Synchronisierungsobjekte in einer Wartefunktion angeben, obwohl ihre Verwendung für diesen Zweck schwierig zu verwalten sein kann, da bei gleichzeitigen überlappenden Vorgängen auf derselben Datei, Named Pipe oder kommunikationsgerät keine Möglichkeit besteht, zu wissen, welcher Vorgang dazu geführt hat, dass der Zustand des Objekts signalisiert wird.

Ein Thread sollte ein Ereignis nicht wiederverwenden, wobei davon ausgegangen wird, dass das Ereignis nur durch den überlappenden Vorgang dieses Threads signalisiert wird. Ein Ereignis wird im selben Thread wie der überlappende Vorgang signalisiert, der abgeschlossen wird. Die Verwendung desselben Ereignisses für mehrere Threads kann zu einer Racebedingung führen, in der das Ereignis für den Thread, dessen Vorgang zuerst abgeschlossen wird, und für andere Threads, die dieses Ereignis verwenden, vorzeitig signalisiert wird. Wenn dann der nächste überlappende Vorgang abgeschlossen ist, wird das Ereignis erneut für alle Threads signalisiert, die dieses Ereignis verwenden, usw., bis alle überlappenden Vorgänge abgeschlossen sind.

Beispiele, die die Verwendung von überlappenden Vorgängen, Vervollständigungsroutinen und der GetOverlappedResult-Funktion veranschaulichen, finden Sie unter Verwenden von Pipes.

Windows Vista, Windows Server 2003 und Windows XP:

Gehen Sie bei der Wiederverwendung von OVERLAPPED-Strukturen vorsichtig vor. Wenn OVERLAPPED-Strukturen in mehreren Threads wiederverwendet werden und GetOverlappedResult aufgerufen wird, wobei der bWait-Parameter auf TRUE festgelegt ist, muss der aufrufende Thread sicherstellen, dass das zugeordnete Ereignis signalisiert wird, bevor die Struktur wiederverwendet wird. Dies kann mithilfe der WaitForSingleObject-Funktion nach dem Aufruf von GetOverlappedResult erreicht werden, um zu erzwingen, dass der Thread wartet, bis der Vorgang abgeschlossen ist. Beachten Sie, dass das Ereignisobjekt ein Ereignisobjekt mit manueller Zurücksetzung sein muss. Wenn ein Autoreset-Ereignisobjekt verwendet wird, wird die Funktion durch Aufrufen von GetOverlappedResult mit dem auf TRUE festgelegten bWait-Parameter blockiert. Dieses Verhalten hat sich ab Windows 7 und Windows Server 2008 R2 für Anwendungen geändert, die Windows 7 als unterstütztes Betriebssystem im Anwendungsmanifest angeben. Weitere Informationen finden Sie unter Anwendungsmanifeste.

E/A-Konzepte