Synchronisierung und überlappende Eingabe und Ausgabe
Sie können 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, ConnectNamedPipeund 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, nachdem der Vorgang abgeschlossen wurde. Dies bedeutet, dass die Ausführung des aufrufenden Threads für einen unbestimmten Zeitraum 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 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 Wait-Funktionen, um zu bestimmen, wann der überlappende Vorgang abgeschlossen wurde. Sie können auch das Makro HasOverlappedIoCompleted verwenden, um den Abschluss abzufragen.
Um alle ausstehenden asynchronen E/A-Vorgänge abzubrechen, verwenden Sie die CancelIoEx-Funktion und stellen 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, die 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 mit 0 (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 Zustand des Ereignisobjekts auf nicht signalisiert fest, wenn ein Aufruf der E/A-Funktion zurückgegeben wird, bevor der Vorgang abgeschlossen wurde. Das System legt den Zustand des Ereignisobjekts so fest, dass er 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, kann der Vorgang abgeschlossen sein, 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, lautet der Rückgabewert der Funktion FALSE, und die GetLastError-Funktion gibt ERROR IO _ _ PENDING zurück.
Ein Thread kann überlappende Vorgänge mit einer von zwei 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 eine warnungsfähige Wartezeit ausführen.
- Geben Sie ein Handle für das Ereignisobjekt für manuelles Zurücksetzen der OVERLAPPED-Struktur in einer der Wartefunktionen an, und rufen Sie dann getOverlappedResult oder GetOverlappedResultExauf, nachdem die Wait-Funktion zurückgegeben wurde. Die Funktion gibt die Ergebnisse des abgeschlossenen überlappenden Vorgangs zurück. Für Funktionen, in denen solche Informationen geeignet sind, meldet sie die tatsächliche Anzahl der übertragenen Bytes.
Wenn mehrere gleichzeitig überlappende Vorgänge für einen einzelnen Thread ausgeführt werden, muss der aufrufende Thread für jeden Vorgang eine OVERLAPPED-Struktur angeben. Jede OVERLAPPED-Struktur muss ein Handle für ein anderes Ereignisobjekt mit manueller Zurücksetzung angeben. Um auf den Abschluss eines der überlappenden Vorgänge zu warten, gibt der Thread alle Manuell zurücksetzungsereignishandles als Wartekriterien in einer der Wartefunktionen mit mehreren Objekten an. Der Rückgabewert der Wait-Funktion mit mehreren Objekten gibt an, welches Ereignisobjekt mit manueller Zurücksetzung signalisiert wurde, sodass der Thread bestimmen kann, welcher überlappende Vorgang den Abschluss des Wartevorgangs verursacht hat.
Es ist sicherer, für jeden überlappende 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 deren Verwendung für diesen Zweck schwierig zu verwalten sein kann, da beim Ausführen gleichzeitig überlappender Vorgänge auf demselben Datei-, Named Pipe- oder Kommunikationsgerät nicht bekannt ist, welcher Vorgang dazu geführt hat, dass der Zustand des Objekts signalisiert wird.
Ein Thread sollte ein Ereignis nicht mit der Annahme wiederverwenden, dass das Ereignis nur durch den überlappenden Vorgang dieses Threads signalisiert wird. Ein Ereignis wird für denselben Thread wie der überlappende Vorgang signalisiert, der abgeschlossen wird. Die Verwendung desselben Ereignisses für mehrere Threads kann zu einer Racebedingung führen, bei der das Ereignis für den Thread, dessen Vorgang zuerst abgeschlossen wird, und vorzeitig für andere Threads, die dieses Ereignis verwenden, ordnungsgemäß signalisiert wird. Wenn dann der nächste überlappende Vorgang abgeschlossen ist, wird das Ereignis für alle Threads, die dieses Ereignis verwenden, erneut signalisiert 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: **
Seien Sie vorsichtig, wenn Sie OVERLAPPED-Strukturen wiederverwenden. Wenn OVERLAPPED-Strukturen in mehreren Threads wiederverwendet werden und GetOverlappedResult mit dem auf TRUE festgelegten bWait-Parameter aufgerufen wird, muss der aufrufende Thread sicherstellen, dass das zugeordnete Ereignis signalisiert wird, bevor die Struktur wiederverwendet wird. Dies kann erreicht werden, indem die WaitForSingleObject-Funktion nach dem Aufruf von GetOverlappedResult verwendet wird, 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, führt der Aufruf von GetOverlappedResult mit dem auf TRUE festgelegten bWait-Parameter dazu, dass die Funktion unbegrenzt blockiert wird. 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.