Delen via


I/O-voltooiingspoorten

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.

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 bestandsingangen is voltooid, wordt een I/O-voltooiingspakket in de wachtrij geplaatst in fifo-volgorde (first-in-first-out) naar de bijbehorende I/O-voltooiingspoort. 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 terwijl de pakketten in de wachtrij worden geplaatst in FIFO-volgorde, ze in een andere volgorde kunnen worden ontkend.

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, benoemde pijp of e-mailsite 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 wordt geretourneerd met een fout. 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 wordt vrijgegeven aan een thread, het systeem de laatste (meest recente) thread die aan die poort is gekoppeld, de voltooiingsgegevens doorgeeft voor de oudste I/O-voltooiing.

Hoewel een willekeurig aantal threads GetQueuedCompletionStatus voor een opgegeven I/O-voltooiingspoort kan aanroepen wanneer een opgegeven thread GetQueuedCompletionStatus de eerste keer, wordt deze gekoppeld aan de opgegeven I/O-voltooiingspoort totdat een van de drie dingen optreedt: de thread wordt afgesloten, geeft een andere I/O-voltooiingspoort op, of sluit de I/O-voltooiingspoort. Met andere woorden, één thread kan worden gekoppeld aan maximaal één I/O-voltooiingspoort.

Wanneer een voltooiingspakket in de wachtrij wordt geplaatst op een I/O-voltooiingspoort, controleert het systeem eerst hoeveel threads aan die poort worden uitgevoerd. 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 voltooit, wordt deze doorgaans GetQueuedCompletionStatus opnieuw aangeroepen. Op dat moment wordt het geretourneerd met het volgende voltooiingspakket of wordt gewacht 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 voor het melden van werkrolthreads van externe gebeurtenissen.

De I/O-voltooiingspoortgreep en elke bestandsingang die is gekoppeld aan die specifieke I/O-voltooiingspoort, wordt verwijzingen naar de I/O-voltooiingspoortgenoemd. De I/O-voltooiingspoort wordt vrijgegeven wanneer er geen verwijzingen meer naar worden verwezen. 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 CloseHandle--functie 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 ingang is echter deelbaar tussen threads in 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 runnable threads dat is gekoppeld aan de voltooiingspoort. 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 met een gelijktijdigheidswaarde van een en meerdere threads die wachten in de GetQueuedCompletionStatus functieaanroep. 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 uit te voeren, maar dat ervan uitgaat dat de actieve thread nooit een wachtstatus krijgt door een ander mechanisme, wordt beëindigd of anderszins de bijbehorende I/O-voltooiingspoort 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 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 SuspendThread--functie. 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 OVERLAPPENDE structuur en een bestandsgreep die eerder is gekoppeld aan een I/O-voltooiingspoort (door een aanroep naar CreateIoCompletionPort) om het I/O-voltooiingspoortmechanisme in te schakelen:

over processen en threads

BindIoCompletionCallback-

CreateIoCompletionPort

GetQueuedCompletionStatus

GetQueuedCompletionStatusEx-

PostQueuedCompletionStatus