Dela via


I/O-slutförandeportar

I/O-slutförandeportar ger en effektiv trådningsmodell för bearbetning av flera asynkrona I/O-begäranden i ett system med flera processorer. När en process skapar en I/O-slutförandeport skapar systemet ett associerat köobjekt för trådar vars enda syfte är att betjäna dessa begäranden. Processer som hanterar många samtidiga asynkrona I/O-begäranden kan göra det snabbare och effektivare genom att använda I/O-slutförandeportar tillsammans med en förallokerad trådpool än genom att skapa trådar när de tar emot en I/O-begäran.

Så här fungerar I/O-slutförandeportar

Funktionen CreateIoCompletionPort skapar en I/O-slutförandeport och associerar en eller flera filreferenser med den porten. När en asynkron I/O-åtgärd på en av dessa filreferenser har slutförts placeras ett I/O-slutförandepaket i FIFO-ordning (first-in-first-out) till den associerade I/O-slutförandeporten. En kraftfull användning för den här mekanismen är att kombinera synkroniseringspunkten för flera filhandtag till ett enda objekt, även om det också finns andra användbara program. Observera att även om paketen placeras i kö i FIFO-ordning kan de vara borttagna i en annan ordning.

Not

Termen filreferens som används här refererar till en systemabstraktion som representerar en överlappande I/O-slutpunkt, inte bara en fil på disken. Det kan till exempel vara en nätverksslutpunkt, TCP-socket, med namnet pipe eller e-postfack. Alla systemobjekt som stöder överlappande I/O kan användas. En lista över relaterade I/O-funktioner finns i slutet av det här avsnittet.

 

När en filreferens är associerad med en slutförandeport uppdateras inte statusblocket som skickas in förrän paketet tas bort från slutförandeporten. Det enda undantaget är om den ursprungliga åtgärden returnerar synkront med ett fel. En tråd (antingen en som skapats av huvudtråden eller själva huvudtråden) använder funktionen GetQueuedCompletionStatus för att vänta tills ett slutförandepaket har placerats i kö till I/O-slutförandeporten i stället för att vänta direkt på att den asynkrona I/O ska slutföras. Trådar som blockerar körningen på en I/O-slutförandeport släpps i LIFO-ordning (last-in-first-out) och nästa slutförandepaket hämtas från I/O-slutförandeportens FIFO-kö för den tråden. Det innebär att när ett slutförandepaket släpps till en tråd släpper systemet den sista (senaste) tråden som är associerad med den porten och skickar den slutförandeinformationen för den äldsta I/O-slutförandet.

Även om valfritt antal trådar kan anropa GetQueuedCompletionStatus för en angiven I/O-slutförandeport, när en angiven tråd anropar GetQueuedCompletionStatus första gången, associeras den med den angivna I/O-slutförandeporten tills en av tre saker inträffar: Tråden avslutas, anger en annan I/O-slutförandeport, eller stänger I/O-slutförandeporten. Med andra ord kan en enda tråd associeras med högst en I/O-slutförandeport.

När ett slutförandepaket placeras i kö till en I/O-slutförandeport kontrollerar systemet först hur många trådar som är associerade med den porten. Om antalet trådar som körs är mindre än samtidighetsvärdet (beskrivs i nästa avsnitt) tillåts en av de väntande trådarna (den senaste) bearbeta slutförandepaketet. När en tråd som körs slutför bearbetningen anropas vanligtvis GetQueuedCompletionStatus igen. Då returneras den antingen med nästa slutförandepaket eller väntar om kön är tom.

Trådar kan använda funktionen PostQueuedCompletionStatus för att placera kompletteringspaket i en I/O-slutförandeports kö. På så sätt kan slutförandeporten användas för att ta emot kommunikation från andra trådar i processen, förutom att ta emot I/O-slutförandepaket från I/O-systemet. Med funktionen PostQueuedCompletionStatus kan ett program köa sina egna paket för specialändamål till I/O-slutförandeporten utan att starta en asynkron I/O-åtgärd. Detta är användbart för att till exempel meddela arbetstrådar om externa händelser.

Porthandtaget för I/O-slutförande och varje filreferens som är associerad med den specifika I/O-slutförandeporten kallas referenser till I/O-slutförandeporten. I/O-slutförandeporten släpps när det inte finns några fler referenser till den. Därför måste alla dessa referenser vara ordentligt stängda för att frigöra I/O-slutförandeporten och dess associerade systemresurser. När dessa villkor är uppfyllda bör ett program stänga I/O-slutförandeporthandtaget genom att anropa funktionen CloseHandle.

Not

En I/O-slutförandeport är associerad med processen som skapade den och kan inte delas mellan processer. Ett enda handtag kan dock delas mellan trådar i samma process.

 

Trådar och samtidighet

Den viktigaste egenskapen för en I/O-slutförandeport att överväga noggrant är samtidighetsvärdet. Samtidighetsvärdet för en slutförandeport anges när den skapas med CreateIoCompletionPort via parametern NumberOfConcurrentThreads. Det här värdet begränsar antalet runnable-trådar som är associerade med slutförandeporten. När det totala antalet runnable-trådar som är associerade med slutförandeporten når samtidighetsvärdet blockerar systemet körningen av efterföljande trådar som är associerade med den slutförandeporten tills antalet runnable trådar sjunker under samtidighetsvärdet.

Det mest effektiva scenariot inträffar när det finns slutförandepaket som väntar i kön, men inga väntetider kan uppfyllas eftersom porten har nått sin samtidighetsgräns. Tänk på vad som händer med ett samtidighetsvärde för en och flera trådar som väntar i funktionsanropet GetQueuedCompletionStatus. I det här fallet, om kön alltid har slutförda paket som väntar, när tråden som körs anropar GetQueuedCompletionStatus, blockeras inte körningen eftersom trådkön, som tidigare nämnts, är LIFO. I stället hämtar den här tråden omedelbart nästa paket för slutförande i kö. Inga trådkontextväxlar inträffar eftersom den tråd som körs kontinuerligt plockar upp kompletteringspaket och de andra trådarna inte kan köras.

Not

I föregående exempel verkar de extra trådarna vara värdelösa och aldrig köras, men det förutsätter att den löpande tråden aldrig hamnar i väntetillstånd av någon annan mekanism, avslutar eller på annat sätt stänger sin associerade I/O-slutförandeport. Tänk på alla sådana trådkörningsförgreningar när du utformar programmet.

 

Det bästa övergripande maximala värdet som ska väljas för samtidighetsvärdet är antalet processorer på datorn. Om transaktionen kräver en lång beräkning tillåter ett större samtidighetsvärde fler trådar att köras. Varje slutförandepaket kan ta längre tid att slutföra, men fler slutförandepaket bearbetas samtidigt. Du kan experimentera med samtidighetsvärdet tillsammans med profileringsverktyg för att uppnå bästa möjliga effekt för ditt program.

Systemet tillåter också att en tråd som väntar i GetQueuedCompletionStatus bearbeta ett slutförandepaket om en annan tråd som körs som är associerad med samma I/O-slutförandeport anger ett väntetillstånd av andra skäl, till exempel funktionen SuspendThread. När tråden i väntetillståndet börjar köras igen kan det finnas en kort period när antalet aktiva trådar överskrider samtidighetsvärdet. Systemet minskar dock snabbt det här antalet genom att inte tillåta några nya aktiva trådar förrän antalet aktiva trådar hamnar under samtidighetsvärdet. Det här är en anledning till att programmet skapar fler trådar i sin trådpool än samtidighetsvärdet. Hantering av trådpooler ligger utanför omfånget för det här ämnet, men en bra tumregel är att ha minst dubbelt så många trådar i trådpoolen som det finns processorer i systemet. Mer information om trådpooler finns i trådpooler.

I/O-funktioner som stöds

Följande funktioner kan användas för att starta I/O-åtgärder som slutförs med hjälp av I/O-slutförandeportar. Du måste skicka funktionen till en instans av OVERLAPPED- struktur och en filreferens som tidigare associerats med en I/O-slutförandeport (genom ett anrop till CreateIoCompletionPort) för att aktivera I/O-slutförandeportmekanismen:

Om processer och trådar

BindIoCompletionCallback

CreateIoCompletionPort

GetQueuedCompletionStatus

GetQueuedCompletionStatusEx

PostQueuedCompletionStatus