Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
I/O-voltooiingspoorten bieden een efficiënt threadingmodel voor het verwerken van meerdere asynchrone I/O-aanvragen op een multiprocessorsysteem. Wanneer een proces een I/O-voltooiingspoort maakt, maakt het systeem een gekoppeld wachtrijobject voor threads waarvan het enige doel is om deze aanvragen te verwerken. Processen die veel gelijktijdige asynchrone I/O-aanvragen verwerken, kunnen dit sneller en efficiënter doen door I/O-voltooiingspoorten te gebruiken in combinatie met een vooraf toegewezen threadgroep dan door threads te maken op het moment dat ze een I/O-aanvraag ontvangen.
Wanneer I/O-voltooiingspoorten gebruiken
| Scenario | Aanbevolen aanpak |
|---|---|
| Server met hoge prestaties verwerken honderden/duizenden gelijktijdige verbindingen | I/O-voltooiingspoorten , speciaal ontworpen voor dit. De kernel beheert de planning van threads om deze af te stemmen op de gelijktijdigheid van de CPU. |
| Matige gelijktijdigheid (tientallen async-bewerkingen) |
Threadpool I/O (CreateThreadpoolIo) - eenvoudigere API, beheert het IOCP intern. Geef de voorkeur aan nieuwe code die geen handmatig thread-besturingselement nodig heeft. |
| Eenvoudige asynchrone bestandsbewerkingen in moderne C++ | C++20 coroutines met een aangepaste IOCP-dispatcher, of .NET FileStream met async/await. |
| I/O met één thread of laag volume | Synchrone I/O of eenvoudige overlappende I/O met gebeurtenissignalering. IOCP voegt onnodige complexiteit toe voor scenario's met één stroom. |
Notitie
Threadpool-API versus onbewerkte IOCP: de Windows threadpool-API (CreateThreadpoolIo, StartThreadpoolIo) maakt intern gebruik van IOCP, maar verwerkt het levenscyclusbeheer van threads automatisch. Voor nieuwe servertoepassingen moet u eerst de THREAD-pool-API overwegen. Deze biedt dezelfde schaalbaarheid met minder standaardcode. Gebruik onbewerkte IOCP wanneer u expliciet controle nodig hebt over de gelijktijdigheidswaarde van de voltooiingspoort of aangepast threadbeheer.
Hoe I/O voltooiingspoorten werken
De functie CreateIoCompletionPort maakt een I/O-voltooiingspoort en koppelt een of meer bestandsingangen aan die poort. Wanneer een asynchrone I/O-bewerking op een van deze bestandshandles wordt voltooid, wordt een I/O-voltooiingspakket in first-in-first-out (FIFO)-volgorde in de wachtrij voor de bijbehorende I/O-voltooiingspoort geplaatst. Een krachtig gebruik voor dit mechanisme is het synchronisatiepunt voor meerdere bestandsingangen te combineren tot één object, hoewel er ook andere nuttige toepassingen zijn. Houd er rekening mee dat, hoewel de pakketten in FIFO-volgorde in de wachtrij worden geplaatst, ze in een andere volgorde uit de wachtrij kunnen worden gehaald.
Notitie
De term bestandsgreep zoals hier wordt gebruikt, verwijst naar een systeemabstractie die een overlappend I/O-eindpunt vertegenwoordigt, niet alleen een bestand op schijf. Het kan bijvoorbeeld een netwerkeindpunt, TCP-socket, named pipe of mailslot zijn. Elk systeemobject dat ondersteuning biedt voor overlappende I/O kan worden gebruikt. Zie het einde van dit onderwerp voor een lijst met gerelateerde I/O-functies.
Wanneer een bestandsingang is gekoppeld aan een voltooiingspoort, wordt het statusblok dat wordt doorgegeven pas bijgewerkt nadat het pakket is verwijderd uit de voltooiingspoort. De enige uitzondering is als de oorspronkelijke bewerking synchroon een foutmelding oplevert. Een thread (een thread die is gemaakt door de hoofdthread of de hoofdthread zelf) gebruikt de GetQueuedCompletionStatus functie om te wachten totdat een voltooiingspakket in de wachtrij wordt geplaatst bij de I/O-voltooiingspoort, in plaats van direct te wachten tot de asynchrone I/O is voltooid. Threads die de uitvoering op een I/O-voltooiingspoort blokkeren, worden vrijgegeven in de LAATSTE IN-First-Out-volgorde (LIFO) en het volgende voltooiingspakket wordt opgehaald uit de FIFO-wachtrij van de I/O-voltooiingspoort voor die thread. Dit betekent dat, wanneer een voltooiingspakket aan een thread wordt vrijgegeven, het systeem de laatste (meest recente) thread vrijgeeft die aan die poort is gekoppeld, en daarbij de voltooiingsinformatie voor de oudste I/O-voltooiing doorgeeft.
Hoewel een willekeurig aantal threads GetQueuedCompletionStatus voor een opgegeven I/O-voltooiingspoort kan aanroepen, wanneer een opgegeven thread GetQueuedCompletionStatus de eerste keer aanroept, wordt deze gekoppeld aan de opgegeven I/O-voltooiingspoort totdat een van de drie dingen optreedt: de thread wordt afgesloten, een andere I/O-voltooiingspoort opgeeft of de I/O-voltooiingspoort sluit. Met andere woorden, één thread kan worden gekoppeld aan maximaal één I/O-voltooiingspoort.
Wanneer een voltooiingspakket in de wachtrij voor een I/O-voltooiingspoort wordt geplaatst, controleert het systeem eerst hoeveel threads die aan die poort zijn gekoppeld actief zijn. Als het aantal threads dat wordt uitgevoerd kleiner is dan de gelijktijdigheidswaarde (besproken in de volgende sectie), mag een van de wachtende threads (de meest recente) het voltooiingspakket verwerken. Wanneer een actieve thread de verwerking heeft voltooid, wordt meestal GetQueuedCompletionStatus opnieuw aangeroepen, waarna deze wordt geretourneerd met het volgende voltooiingspakket of wacht als de wachtrij leeg is.
Threads kunnen de functie PostQueuedCompletionStatus gebruiken om voltooiingspakketten in de wachtrij van een I/O-voltooiingspoort te plaatsen. Hierdoor kan de voltooiingspoort worden gebruikt om communicatie te ontvangen van andere threads van het proces, naast het ontvangen van I/O-voltooiingspakketten van het I/O-systeem. Met de functie PostQueuedCompletionStatus kan een toepassing zijn eigen speciale voltooiingspakketten in de wachtrij plaatsen bij de I/O-voltooiingspoort zonder een asynchrone I/O-bewerking te starten. Dit is bijvoorbeeld handig om workerthreads op de hoogte te stellen van externe gebeurtenissen.
De handle van de I/O-voltooiingspoort en elke bestandshandle die is gekoppeld aan die specifieke I/O-voltooiingspoort worden verwijzingen naar de I/O-voltooiingspoort genoemd. De I/O-completionpoort wordt vrijgegeven wanneer er geen verwijzingen ernaar meer zijn. Daarom moeten al deze ingangen correct worden gesloten om de I/O-voltooiingspoort en de bijbehorende systeembronnen vrij te geven. Nadat aan deze voorwaarden is voldaan, moet een toepassing de I/O-voltooiingspoorthandgreep sluiten door de functie CloseHandle aan te roepen.
Notitie
Een I/O-voltooiingspoort is gekoppeld aan het proces dat het heeft gemaakt en kan niet worden verdeeld tussen processen. Een enkele handle kan echter worden gedeeld tussen threads binnen hetzelfde proces.
Threads en gelijktijdigheid
De belangrijkste eigenschap van een I/O-voltooiingspoort die zorgvuldig moet worden overwogen, is de gelijktijdigheidswaarde. De gelijktijdigheidswaarde van een voltooiingspoort wordt opgegeven wanneer deze wordt gemaakt met CreateIoCompletionPort via de parameter NumberOfConcurrentThreads . Deze waarde beperkt het aantal uitvoerbare threads dat aan de voltooiingspoort is gekoppeld. Wanneer het totale aantal runnable threads dat is gekoppeld aan de voltooiingspoort de gelijktijdigheidswaarde bereikt, blokkeert het systeem de uitvoering van eventuele volgende threads die zijn gekoppeld aan die voltooiingspoort totdat het aantal runnable threads onder de gelijktijdigheidswaarde daalt.
Het meest efficiënte scenario treedt op wanneer er voltooiingspakketten in de wachtrij wachten, maar er kunnen geen wachttijden worden voldaan omdat de poort de gelijktijdigheidslimiet heeft bereikt. Bedenk wat er gebeurt wanneer de gelijktijdigheidswaarde één is en er meerdere threads wachten bij de functieaanroep GetQueuedCompletionStatus. In dit geval, als de wachtrij altijd voltooiingspakketten heeft die wachten, wanneer de actieve thread aanroept GetQueuedCompletionStatus, wordt de uitvoering niet geblokkeerd omdat, zoals eerder vermeld, de threadwachtrij LIFO is. In plaats daarvan haalt deze thread onmiddellijk het volgende voltooiingspakket in de wachtrij op. Er treden geen threadcontextswitches op, omdat de actieve thread voortdurend voltooiingspakketten ophaalt en de andere threads niet kunnen worden uitgevoerd.
Notitie
In het vorige voorbeeld lijken de extra threads nutteloos te zijn en nooit te worden uitgevoerd, maar daarbij wordt ervan uitgegaan dat de actieve thread nooit door een ander mechanisme in een wachtstatus wordt geplaatst, wordt beëindigd of anderszins het bijbehorende I/O-completionport sluit. Houd rekening met al deze threaduitvoeringsvertakkingen bij het ontwerpen van de toepassing.
De beste totale maximumwaarde voor de gelijktijdigheidswaarde is het aantal CPU's op de computer. Als uw transactie een langdurige berekening vereist, kan een grotere gelijktijdigheidswaarde meer threads uitvoeren. Het kan langer duren voordat elk voltooiingspakket is voltooid, maar meer voltooiingspakketten worden tegelijkertijd verwerkt. U kunt experimenteren met de gelijktijdigheidswaarde in combinatie met profileringshulpprogramma's om het beste effect voor uw toepassing te bereiken.
Het systeem staat ook een thread toe die wacht in GetQueuedCompletionStatus om een voltooiingspakket te verwerken als een andere actieve thread die is gekoppeld aan dezelfde I/O-voltooiingspoort, een wachtstatus invoert om andere redenen, bijvoorbeeld de functie SuspendThread . Wanneer de thread in de wachtstatus opnieuw wordt uitgevoerd, kan er een korte periode zijn wanneer het aantal actieve threads de gelijktijdigheidswaarde overschrijdt. Het systeem vermindert dit aantal echter snel door nieuwe actieve threads pas toe te staan als het aantal actieve threads onder de gelijktijdigheidswaarde valt. Dit is een van de redenen om ervoor te zorgen dat uw toepassing meer threads in de threadgroep maakt dan de gelijktijdigheidswaarde. Het beheer van threadgroepen valt buiten het bereik van dit onderwerp, maar een goede vuistregel is om minimaal twee keer zoveel threads in de threadgroep te hebben als er processors in het systeem zijn. Zie Thread Poolsvoor meer informatie over threadpools.
Ondersteunde I/O-functies
De volgende functies kunnen worden gebruikt om I/O-bewerkingen te starten die zijn voltooid met behulp van I/O-voltooiingspoorten. U moet de functie doorgeven aan een exemplaar van de OVERLAPPED-structuur en een bestandsgreep die eerder is gekoppeld aan een I/O-voltooiingspoort (door een aanroep van CreateIoCompletionPort) om het I/O-voltooiingspoortmechanisme in te schakelen:
- AcceptEx
- ConnectNamedPipe
- DeviceIoControl
- LockFileEx
- ReadDirectoryChangesW
- ReadFile
- TransactNamedPipe
- WaitCommEvent
- WriteFile
- WSASendMsg
- WSASendTo
- WSASend
- WSARecvFrom
- LPFN_WSARECVMSG (WSARecvMsg)
- WSARecv