Porty pro dokončování vstupně-výstupních operací
Porty pro dokončování vstupně-výstupních operací poskytují efektivní model vláken pro zpracování více asynchronních vstupně-výstupních požadavků v multiprocesorovém systému. Když proces vytvoří port pro dokončení vstupně-výstupních operací, vytvoří systém přidružený objekt fronty pro vlákna, jejichž jediným účelem je obsluhovat tyto požadavky. Procesy, které zpracovávají mnoho souběžných asynchronních vstupně-výstupních požadavků, to můžou udělat rychleji a efektivněji pomocí portů pro dokončování vstupně-výstupních operací ve spojení s předem přiděleným fondem vláken než vytvořením vláken v době, kdy obdrží vstupně-výstupní požadavek.
Jak fungují porty pro dokončování vstupně-výstupních operací
Funkce CreateIoCompletionPort vytvoří port pro doplňování vstupně-výstupních operací a přidruží k němu jeden nebo více popisovačů souborů. Když se dokončí asynchronní vstupně-výstupní operace na jednom z těchto popisovačů souborů, zařadí se paket dokončení vstupně-výstupních operací do fronty v pořadí prvního v prvním ven (FIFO) na přidružený port dokončení vstupně-výstupních operací. Jedním z výkonných použití tohoto mechanismu je kombinování synchronizačního bodu pro více popisovačů souborů do jednoho objektu, i když existují i další užitečné aplikace. Upozorňujeme, že zatímco jsou pakety zařazeny do fronty v pořadí FIFO, mohou být vyřazeny z fronty v jiném pořadí.
Poznámka
Termín popisovač souboru, jak se zde používá, odkazuje na abstrakci systému představující překrývající se vstupně-výstupní koncový bod, nejen soubor na disku. Může to být například koncový bod sítě, soket TCP, pojmenovaný kanál nebo slot pošty. Lze použít jakýkoli systémový objekt, který podporuje překrývající se vstupně-výstupní operace. Seznam souvisejících vstupně-výstupních funkcí najdete na konci tohoto tématu.
Pokud je popisovač souboru přidružený k portu dokončení, stavový blok předaný se neaktualizuje, dokud se paket neodebere z portu dokončení. Jedinou výjimkou je, pokud původní operace vrátí synchronně s chybou. Vlákno (buď jedno vytvořené hlavním vláknem, nebo samotné hlavní vlákno) používá funkci GetQueuedCompletionStatus, aby čekala na zařazení paketu dokončení do fronty na port dokončení vstupně-výstupních operací, a nečeká přímo na dokončení asynchronního vstupně-výstupního portu. Vlákna, která blokují provádění na portu pro dokončení vstupně-výstupních operací, se uvolní v pořadí posledního v prvním kroku (LIFO) a další paket dokončení se načte z fronty FIFO portu pro dokončení vstupně-výstupních operací pro toto vlákno. To znamená, že když se paket dokončení uvolní do vlákna, systém uvolní poslední (nejnovější) vlákno přidružené k tomuto portu, které předá informace o dokončení nejstaršího vstupně-výstupního dokončení.
I když libovolný počet vláken může volat GetQueuedCompletionStatus pro zadaný vstupně-výstupní port, když zadané vlákno volá GetQueuedCompletionStatus poprvé, stane se přidružený k zadanému portu pro dokončení vstupně-výstupních operací, dokud nedojde k jedné ze tří věcí: Vlákno ukončí, určuje jiný vstupně-výstupní port dokončení, nebo zavře port dokončení vstupně-výstupních operací. Jinými slovy, k jednomu portu dokončení vstupně-výstupních operací může být přidruženo jedno vlákno.
Když se paket dokončení zařadí do fronty na port dokončení vstupně-výstupních operací, systém nejprve zkontroluje, kolik vláken přidružených k danému portu je spuštěno. Pokud je počet spuštěných vláken menší než hodnota souběžnosti (probíraná v další části), je povolené zpracování paketu dokončení jedním z čekacích vláken (nejnovějších vláken). Když spuštěné vlákno dokončí své zpracování, obvykle volá GetQueuedCompletionStatus znovu, v tomto okamžiku se vrátí buď s dalším paketem dokončení, nebo čeká, pokud je fronta prázdná.
Vlákna mohou použít funkci PostQueuedCompletionStatus k umístění paketů dokončení do fronty portu pro dokončení vstupně-výstupních operací. Tímto způsobem lze port dokončení použít k příjmu komunikace z jiných vláken procesu, kromě příjmu paketů dokončení vstupně-výstupních operací ze vstupně-výstupního systému. Funkce PostQueuedCompletionStatus umožňuje aplikaci zařadit do fronty vlastní pakety pro dokončení speciálních účelů na port dokončení vstupně-výstupních operací bez spuštění asynchronní vstupně-výstupní operace. To je užitečné například pro upozorňování pracovních vláken na externí události.
Popisovač portu dokončení vstupně-výstupních operací a každý popisovač souboru přidružený k danému portu pro dokončování vstupně-výstupních operací se označuje jako odkazy na port dokončení vstupně-výstupních operací. Port dokončení vstupně-výstupních operací se uvolní, když na něj už nejsou žádné odkazy. Proto musí být všechny tyto popisovače řádně uzavřeny, aby se uvolnil port pro dokončování vstupně-výstupních operací a jeho přidružené systémové prostředky. Po splnění těchto podmínek by aplikace měla zavřít popisovač portu pro dokončování vstupně-výstupních operací voláním funkce CloseHandle.
Poznámka
Port pro dokončování vstupně-výstupních operací je přidružený k procesu, který ho vytvořil, a není mezi procesy šiditelný. Jeden popisovač je však možné přesouvat mezi vlákny ve stejném procesu.
Vlákna a souběžnost
Nejdůležitější vlastností portu pro dokončování vstupně-výstupních operací, které je potřeba pečlivě zvážit, je hodnota souběžnosti. Hodnota souběžnosti portu dokončení je zadána při jeho vytvoření pomocí CreateIoCompletionPort prostřednictvím parametru NumberOfConcurrentThreads. Tato hodnota omezuje počet spuštěných vláken přidružených k portu dokončení. Když celkový počet spuštěných vláken přidružených k portu pro dokončení dosáhne hodnoty souběžnosti, systém zablokuje provádění všech dalších vláken přidružených k danému portu dokončení, dokud počet spuštěných vláken klesne pod hodnotu souběžnosti.
K nejúčinnějšímu scénáři dochází v případě, že ve frontě čekají pakety dokončení, ale není možné splnit žádné čekání, protože port dosáhl limitu souběžnosti. Zvažte, co se stane s hodnotou souběžnosti jednoho a více vláken čekajících v GetQueuedCompletionStatus volání funkce. V tomto případě, pokud fronta vždy obsahuje pakety dokončení čekání, když spuštěné vlákno volá GetQueuedCompletionStatus, nebude blokovat provádění, protože, jak je uvedeno výše, fronta vláken je LIFO. Místo toho toto vlákno okamžitě vyzvedne další paket dokončení ve frontě. Nedojde k žádným přepínačům kontextu vlákna, protože spuštěné vlákno neustále zabírá pakety dokončení a ostatní vlákna se nedají spustit.
Poznámka
V předchozím příkladu se zdá, že nadbytečná vlákna jsou nepoužitá a nikdy neběží, ale předpokládá se, že spuštěné vlákno se nikdy nedostane do stavu čekání jiným mechanismem, ukončí nebo jinak zavře jeho přidružený vstupně-výstupní port dokončení. Při návrhu aplikace zvažte všechny takové důsledky provádění vláken.
Nejlepší celková maximální hodnota, kterou je potřeba vybrat pro hodnotu souběžnosti, je počet procesorů v počítači. Pokud vaše transakce vyžadovala zdlouhavý výpočet, větší hodnota souběžnosti umožní spuštění více vláken. Dokončení každého paketu dokončení může trvat déle, ale současně se zpracuje více paketů dokončení. Můžete experimentovat s hodnotou souběžnosti ve spojení s nástroji pro profilaci, abyste dosáhli nejlepšího efektu pro vaši aplikaci.
Systém také umožňuje vlákno čekající v GetQueuedCompletionStatus zpracovat paket dokončení, pokud jiné spuštěné vlákno přidružené ke stejnému vstupně-výstupnímu portu pro dokončení zadá stav čekání z jiných důvodů, například SuspendThread funkce. Když vlákno ve stavu čekání začne znovu spuštěno, může existovat krátké období, kdy počet aktivních vláken překročí hodnotu souběžnosti. Systém ale toto číslo rychle snižuje tím, že nepovoluje žádná nová aktivní vlákna, dokud počet aktivních vláken klesne pod hodnotu souběžnosti. To je jeden z důvodů, proč aplikace vytvořit více vláken ve fondu vláken než hodnota souběžnosti. Správa fondu vláken je nad rámec tohoto tématu, ale dobrým pravidlem je mít minimálně dvakrát tolik vláken ve fondu vláken, jako jsou procesory v systému. Další informace o sdružování vláken naleznete v tématu fondy vláken.
Podporované vstupně-výstupní funkce
Následující funkce je možné použít ke spuštění vstupně-výstupních operací, které se dokončí pomocí portů pro dokončování vstupně-výstupních operací. Abyste umožnili mechanismus dokončení vstupně-výstupních operací, musíte funkci předat instanci struktury OVERLAPPED a popisovač souboru, který byl dříve přidružený k portu pro dokončování vstupně-výstupních operací (voláním příkazu CreateIoCompletionPort).
- AcceptEx
- ConnectNamedPipe
- DeviceIoControl
- LockFileEx
- ReadDirectoryChangesW
- readfile
- TransactNamedPipe
- WaitCommEvent
- writefile
- WSASendMsg
- WSASendTo
- WSASend
- WSARecvFrom
- LPFN_WSARECVMSG (WSARecvMsg)
- WSARecv
Související témata