Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
E/A-Vervollständigungsports bieten ein effizientes Threadingmodell für die Verarbeitung mehrerer asynchroner E/A-Anforderungen auf einem Multiprozessorsystem. Wenn ein Prozess einen E/A-Vervollständigungsport erstellt, erstellt das System ein zugeordnetes Warteschlangenobjekt für Threads, deren einziger Zweck darin besteht, diese Anforderungen zu verarbeiten. Prozesse, die viele gleichzeitige asynchrone E/A-Anforderungen verarbeiten, können dies schneller und effizienter tun, indem E/A-Vervollständigungsports in Verbindung mit einem vorab zugewiesenen Threadpool verwendet werden, als zum Zeitpunkt der Übermittlung einer E/A-Anforderung Threads zu erstellen.
Funktionsweise von E/A-Abschlussports
Die CreateIoCompletionPort-Funktion erstellt einen E/A-Vervollständigungsport und ordnet einen oder mehrere Dateihandles diesem Port zu. Wenn ein asynchroner E/A-Vorgang auf einem dieser Dateihandles abgeschlossen ist, wird ein E/A-Vervollständigungspaket in die Warteschlange in der FiFO-Reihenfolge (First-in-First-Out) an den zugeordneten E/A-Abschlussport gestellt. Eine leistungsstarke Verwendung für diesen Mechanismus besteht darin, den Synchronisierungspunkt für mehrere Dateihandles in einem einzelnen Objekt zu kombinieren, obwohl es auch andere nützliche Anwendungen gibt. Bitte beachten Sie, dass während die Pakete in der FIFO-Reihenfolge in die Warteschlange gestellt werden, sie möglicherweise in einer anderen Reihenfolge dequeuiert werden.
Anmerkung
Der Begriff Dateihandle, wie hier verwendet, bezieht sich auf eine Systemstraktion, die einen überlappenden E/A-Endpunkt darstellt, nicht nur eine Datei auf dem Datenträger. Beispielsweise kann es sich um einen Netzwerkendpunkt, einen TCP-Socket, einen benannten Pipe- oder einen E-Mail-Steckplatz handeln. Jedes Systemobjekt, das überlappende E/A unterstützt, kann verwendet werden. Eine Liste verwandter E/A-Funktionen finden Sie am Ende dieses Themas.
Wenn ein Dateihandle einem Vervollständigungsport zugeordnet ist, wird der übergebene Statusblock erst aktualisiert, wenn das Paket aus dem Abschlussport entfernt wird. Die einzige Ausnahme ist, wenn der ursprüngliche Vorgang synchron mit einem Fehler zurückgegeben wird. Ein Thread (entweder ein Thread, der vom Hauptthread oder dem Hauptthread selbst erstellt wurde) verwendet den GetQueuedCompletionStatus--Funktion, um zu warten, dass ein Abschlusspaket an den E/A-Abschlussport in die Warteschlange gestellt wird, anstatt direkt auf den Abschluss der asynchronen E/A zu warten. Threads, die ihre Ausführung an einem E/A-Abschlussport blockieren, werden in der Reihenfolge der letzten in first-out (LIFO) freigegeben, und das nächste Abschlusspaket wird aus der FIFO-Warteschlange des E/A-Abschlussports für diesen Thread abgerufen. Dies bedeutet, dass das System, wenn ein Vervollständigungspaket für einen Thread freigegeben wird, den letzten (aktuellsten) Thread freigibt, der diesem Port zugeordnet ist, die Abschlussinformationen für den ältesten E/A-Abschluss übergibt.
Obwohl eine beliebige Anzahl von Threads GetQueuedCompletionStatus für einen angegebenen E/A-Vervollständigungsport aufrufen kann, wenn ein angegebener Thread GetQueuedCompletionStatus zum ersten Mal aufruft, wird er dem angegebenen E/A-Abschlussport zugeordnet, bis eines von drei Dingen auftritt: Der Thread beendet, gibt einen anderen E/A-Vervollständigungsport an oder schließt den E/A-Vervollständigungsport. Mit anderen Worten, ein einzelner Thread kann höchstens einem E/A-Vervollständigungsport zugeordnet werden.
Wenn ein Vervollständigungspaket an einem E/A-Abschlussport in die Warteschlange gestellt wird, überprüft das System zunächst, wie viele Threads mit diesem Port verbunden sind. Wenn die Anzahl der ausgeführten Threads kleiner als der Parallelitätswert ist (im nächsten Abschnitt erläutert), kann eines der wartenden Threads (die neueste Threads) das Abschlusspaket verarbeiten. Wenn ein laufender Thread die Verarbeitung abgeschlossen hat, ruft er in der Regel erneut GetQueuedCompletionStatus auf, woraufhin entweder das nächste Abschlusspaket empfangen wird oder er wartet, wenn die Warteschlange leer ist.
Threads können die PostQueuedCompletionStatus-Funktion verwenden, um Abschlusspakete in die Abschlusswarteschlange eines E/A-Abschlussports einzureihen. Dadurch kann der Abschlussport verwendet werden, um Kommunikationen von anderen Threads des Prozesses zu empfangen, zusätzlich zum Empfangen von E/A-Vervollständigungspaketen aus dem E/A-System. Mit der PostQueuedCompletionStatus--Funktion kann eine Anwendung ihre eigenen speziellen Vervollständigungspakete an den E/A-Abschlussport in die Warteschlange stellen, ohne einen asynchronen E/A-Vorgang zu starten. Dies ist beispielsweise hilfreich, um Arbeitsthreads über externe Ereignisse zu benachrichtigen.
Das E/A-Vervollständigungsporthandle und jedes Dateihandle, das diesem bestimmten E/A-Vervollständigungsport zugeordnet ist, werden als Verweise auf den E/A-Vervollständigungsportbezeichnet. Der E/A-Vervollständigungsport wird freigegeben, wenn keine weiteren Verweise darauf vorhanden sind. Daher müssen alle diese Handles ordnungsgemäß geschlossen werden, um den E/A-Abschlussport und die zugehörigen Systemressourcen freizugeben. Nachdem diese Bedingungen erfüllt wurden, sollte eine Anwendung das E/A-Abschlussport-Handle schließen, indem sie die CloseHandle-Funktion aufruft.
Anmerkung
Ein E/A-Vervollständigungsport ist dem Prozess zugeordnet, der ihn erstellt hat und nicht zwischen Prozessen zu verharren ist. Ein einzelner Handle kann jedoch zwischen Threads im selben Prozess freigegeben werden.
Threads und Parallelität
Die wichtigste Eigenschaft eines E/A-Vervollständigungsports, um sorgfältig zu berücksichtigen, ist der Parallelitätswert. Der Parallelitätswert eines Abschlussports wird festgelegt, wenn dieser mit CreateIoCompletionPort und dem Parameter NumberOfConcurrentThreads erstellt wird. Dieser Wert beschränkt die Anzahl der ausgeführten Threads, die dem Abschlussport zugeordnet sind. Wenn die Gesamtanzahl der ausgeführten Threads, die dem Abschlussport zugeordnet sind, den Parallelitätswert erreicht, blockiert das System die Ausführung aller nachfolgenden Threads, die diesem Abschlussport zugeordnet sind, bis die Anzahl der ausgeführten Threads unter den Parallelitätswert fällt.
Das effizienteste Szenario tritt auf, wenn abschlusspakete in der Warteschlange warten, aber keine Wartezeiten erfüllt werden können, da der Port seine Parallelitätsgrenze erreicht hat. Berücksichtigen Sie, was passiert, wenn der Parallelitätswert eins beträgt und mehrere Threads im Aufruf der Funktion GetQueuedCompletionStatus warten. In diesem Fall wird die Ausführung nicht blockiert, wenn die Warteschlange immer abgeschlossene Pakete wartet, wenn der ausgeführte Thread GetQueuedCompletionStatusaufruft, die Ausführung nicht blockiert, da die Threadwarteschlange wie bereits erwähnt LIFO ist. Stattdessen übernimmt dieser Thread sofort das nächste Paket für den Abschluss in der Warteschlange. Es treten keine Threadkontextoptionen auf, da der ausgeführte Thread fortlaufend Abschlusspakete abnimmt und die anderen Threads nicht ausgeführt werden können.
Anmerkung
Im vorherigen Beispiel scheinen die zusätzlichen Threads nutzlos zu sein und nie ausgeführt zu werden, aber es wird davon ausgegangen, dass der ausgeführte Thread nie von einem anderen Mechanismus in einen Wartezustand versetzt wird, beendet oder anderweitig den zugeordneten E/A-Abschlussport schließt. Berücksichtigen Sie beim Entwerfen der Anwendung alle derartigen Threadausführungs-Auswirkungen.
Der beste Gesamthöchstwert, der für den Parallelitätswert ausgewählt werden soll, ist die Anzahl der CPUs auf dem Computer. Wenn ihre Transaktion eine langwierige Berechnung erforderte, kann ein größerer Parallelitätswert mehr Threads ausführen. Jedes Vervollständigungspaket kann länger dauern, aber gleichzeitig werden mehr Vervollständigungspakete verarbeitet. Sie können mit dem Parallelitätswert in Verbindung mit Profilerstellungstools experimentieren, um den besten Effekt für Ihre Anwendung zu erzielen.
Das System ermöglicht auch, dass ein Thread, der in GetQueuedCompletionStatus wartet, ein Vervollständigungspaket verarbeiten kann, wenn ein anderer ausgeführter Thread, der demselben E/A-Vervollständigungsport zugeordnet ist, aus anderen Gründen einen Wartezustand eingibt, z. B. die SuspendThread-Funktion . Wenn der Thread im Wartezustand erneut ausgeführt wird, kann es einen kurzen Zeitraum geben, in dem die Anzahl der aktiven Threads den Parallelitätswert überschreitet. Das System reduziert diese Zahl jedoch schnell, indem keine neuen aktiven Threads zugelassen werden, bis die Anzahl der aktiven Threads unter den Parallelitätswert fällt. Dies ist ein Grund dafür, dass Ihre Anwendung mehr Threads in seinem Threadpool erstellt, als der Parallelitätswert. Threadpoolverwaltung geht über den Umfang dieses Themas hinaus, aber eine gute Faustregel besteht darin, mindestens doppelt so viele Threads im Threadpool zu haben, wie es Prozessoren im System gibt. Weitere Informationen zum Threadpooling finden Sie unter Threadpools.
Unterstützte E/A-Funktionen
Die folgenden Funktionen können verwendet werden, um E/A-Vorgänge zu starten, die mit E/A-Vervollständigungsports abgeschlossen werden. Sie müssen die Funktion an eine Instanz der OVERLAPPED-Struktur und ein Dateihandle übergeben, das zuvor einem E/A-Vervollständigungsport zugeordnet war (durch einen Aufruf von CreateIoCompletionPort), um den E/A-Vervollständigungsportmechanismus zu aktivieren:
- AcceptEx
- ConnectNamedPipe
- DeviceIoControl-
- LockFileEx-
- ReadDirectoryChangesW-
- ReadFile-
- TransactNamedPipe
- WaitCommEvent-
- WriteFile-
- WSASendMsg
- WSASendTo
- WSASend
- WSARecvFrom
- LPFN_WSARECVMSG (WSARecvMsg)
- WSARecv