E/A-Vervollständigungsports
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-Vervollständigungsports
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 das erste Mal aufruft, wird er dem angegebenen E/A-Vervollständigungsport zugeordnet, bis eines von drei Dingen auftritt: Der Thread beendet, gibt einen anderen E/A-Abschlussport 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 ausgeführter Thread die Verarbeitung abgeschlossen hat, ruft er in der Regel GetQueuedCompletionStatus erneut auf, an dem er entweder mit dem nächsten Abschlusspaket zurückgegeben wird oder wartet, wenn die Warteschlange leer ist.
Threads können die PostQueuedCompletionStatus- Funktion verwenden, um Abschlusspakete in der Warteschlange eines E/A-Vervollständigungsports zu platzieren. 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 sind, sollte eine Anwendung das E/A-Vervollständigungsporthandle 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 Vervollständigungsports wird angegeben, wenn er mit CreateIoCompletionPort- über den NumberOfConcurrentThreads Parameter 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. Überlegen Sie, was mit einem Parallelitätswert von einem und mehreren Threads geschieht, die im GetQueuedCompletionStatus Funktionsaufruf 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 in GetQueuedCompletionStatus- ein Abschlusspaket verarbeiten kann, wenn ein anderer ausgeführter Thread, der demselben E/A-Abschlussport zugeordnet ist, aus anderen Gründen in einen Wartezustand eintritt, 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-Abschlussportmechanismus zu aktivieren:
- AcceptEx-
- ConnectNamedPipe-
- DeviceIoControl-
- LockFileEx-
- ReadDirectoryChangesW-
- ReadFile-
- TransactNamedPipe-
- WaitCommEvent-
- WriteFile-
- WSASendMsg
- WSASendTo
- WSASend-
- WSARecvFrom
- LPFN_WSARECVMSG (WSARecvMsg)
- WSARecv-
Verwandte Themen