I/O-befejezési portok
Az I/O-befejezési portok hatékony menetmodellt biztosítanak több aszinkron I/O-kérés feldolgozásához többprocesszoros rendszeren. Amikor egy folyamat létrehoz egy I/O-befejezési portot, a rendszer létrehoz egy társított üzenetsor-objektumot azokhoz a szálakhoz, amelyek egyetlen célja ezeknek a kéréseknek a kiszolgálása. A számos egyidejű Aszinkron I/O-kérést kezelő folyamatok gyorsabban és hatékonyabban végezhetik el ezt az I/O-befejezési portok előre lefoglalt szálkészlettel együtt történő használatával, mint az I/O-kérések fogadásakor létrehozott szálakkal.
Az I/O-befejezési portok működése
A CreateIoCompletionPort függvény létrehoz egy I/O-befejezési portot, és egy vagy több fájlleírót társít ehhez a porthoz. Ha az egyik fájlleíró aszinkron I/O-művelete befejeződik, egy I/O-befejezési csomag sorba kerül az első előtti (FIFO) sorrendben a társított I/O-befejezési portra. Ennek a mechanizmusnak az egyik hatékony használata, ha több fájlfogópont szinkronizálási pontját egyetlen objektumba egyesíti, bár vannak más hasznos alkalmazások is. Vegye figyelembe, hogy bár a csomagok FIFO sorrendben vannak várólistára állítva, előfordulhat, hogy azokat más sorrendben lehet lekérdezni.
Jegyzet
Az itt használt fájlkezelő kifejezés egy átfedésben lévő I/O-végpontot jelképező rendszer absztrakciójára utal, nem csak a lemezen lévő fájlra. Ez lehet például hálózati végpont, TCP-szoftvercsatorna, elnevezett cső vagy levelezési pont. Minden olyan rendszerobjektum használható, amely támogatja az átfedésben lévő I/O-t. A kapcsolódó I/O-függvények listáját a témakör végén találja.
Ha egy fájlleíró befejezési porthoz van társítva, az átadott állapotblokk mindaddig nem frissül, amíg el nem távolítja a csomagot a befejezési portról. Az egyetlen kivétel az, ha az eredeti művelet szinkron módon, hibával tér vissza. Egy szál (vagy a főszál vagy maga a főszál által létrehozott) a GetQueuedCompletionStatus függvénnyel várja meg, amíg egy befejező csomag várólistára kerül az I/O-befejezési porton, ahelyett, hogy közvetlenül az aszinkron I/O-ra várna. Az I/O-befejezési porton a végrehajtásukat letiltó szálak az utolsó előtti (LIFO) sorrendben jelennek meg, és a következő befejezési csomag le lesz kérve az I/O-befejezési port FIFO-üzenetsorából az adott szálhoz. Ez azt jelenti, hogy amikor egy befejező csomag ki van adva egy szálhoz, a rendszer felszabadítja a porthoz társított utolsó (legújabb) szálat, és átadja neki a legrégebbi I/O-befejezés befejezési adatait.
Bár tetszőleges számú szál meghívhat GetQueuedCompletionStatus egy adott I/O-befejezési porthoz, amikor egy adott szál először GetQueuedCompletionStatus, az a megadott I/O-befejezési porthoz lesz társítva, amíg a három dolog egyike nem következik be: A szál kilép, egy másik I/O befejezési portot ad meg, vagy bezárja az I/O-befejezési portot. Más szóval egyetlen szál legfeljebb egy I/O-befejezési porthoz társítható.
Amikor egy befejező csomag várólistára kerül egy I/O-befejezési portra, a rendszer először ellenőrzi, hogy hány szál fut az adott porthoz társítva. Ha a futó szálak száma kisebb, mint az egyidejűségi érték (a következő szakaszban tárgyaljuk), az egyik várakozó szál (a legutóbbi) feldolgozhatja a befejező csomagot. Amikor egy futó szál befejezi a feldolgozást, általában újra meghívja GetQueuedCompletionStatus, ahol vagy a következő befejező csomaggal tér vissza, vagy várakozik, ha az üzenetsor üres.
A szálak a PostQueuedCompletionStatus függvénnyel befejező csomagokat helyezhetnek el egy I/O-befejezési port üzenetsorában. Ezzel a befejező port a folyamat más szálaitól érkező kommunikáció fogadására használható, amellett, hogy I/O-befejezési csomagokat fogad az I/O-rendszertől. A PostQueuedCompletionStatus függvény lehetővé teszi, hogy az alkalmazás aszinkron I/O-művelet indítása nélkül várólistára tegye saját speciális célú befejezési csomagjait az I/O-befejezési portra. Ez hasznos lehet például a külső események feldolgozói szálainak értesítéséhez.
Az I/O befejezési port leírója és az adott I/O-befejezési porthoz társított összes fájlleíró az I/O befejezési portrahivatkozik. Az I/O-befejezési port akkor lesz felszabadítva, ha nincs több hivatkozás rá. Ezért ezeknek a leíróknak megfelelően le kell zárniuk az I/O-befejezési portot és a hozzá tartozó rendszererőforrásokat. A feltételek teljesülése után az alkalmazásnak be kell zárnia az I/O-befejezési port leíróját a CloseHandle függvény meghívásával.
Jegyzet
Az I/O-befejezési port az azt létrehozó folyamathoz van társítva, és nem tagozható a folyamatok között. Egyetlen fogópont azonban megosztható az ugyanabban a folyamatban lévő szálak között.
Szálak és egyidejűség
Az I/O-befejezési port legfontosabb tulajdonsága, amelyet gondosan figyelembe kell venni, az egyidejűség értéke. A befejezési port egyidejűségi értéke akkor van megadva, ha CreateIoCompletionPortNumberOfConcurrentThreads paraméteren keresztül jön létre. Ez az érték korlátozza a befejezési porthoz társított futtatható szálak számát. Amikor a befejezési porthoz társított futtatható szálak teljes száma eléri az egyidejűségi értéket, a rendszer blokkolja az adott befejezési porthoz társított további szálak végrehajtását, amíg a futtatható szálak száma nem csökken az egyidejűségi érték alá.
A leghatékonyabb forgatókönyv akkor fordul elő, ha befejező csomagok várnak az üzenetsorban, de nem lehet várni, mert a port elérte az egyidejűségi korlátot. Gondolja át, mi történik egy és több szál egyidejűségi értékével a GetQueuedCompletionStatus függvényhívásban. Ebben az esetben, ha az üzenetsor mindig várakozó befejező csomagokkal rendelkezik, amikor a futó szál meghívja GetQueuedCompletionStatus, az nem blokkolja a végrehajtást, mert, ahogy korábban említettük, a szál üzenetsora LIFO. Ehelyett ez a szál azonnal felveszi a következő várólistára helyezett befejezési csomagot. Nem történik szálkörnyezet-kapcsoló, mert a futó szál folyamatosan veszi fel a befejező csomagokat, a többi szál pedig nem tud futni.
Jegyzet
Az előző példában a további szálak haszontalannak tűnnek, és soha nem futnak, de ez azt feltételezi, hogy a futó szál soha nem kerül várakozási állapotba más mechanizmus által, leáll, vagy más módon bezárja a kapcsolódó I/O-befejezési portot. Az alkalmazás tervezésekor vegye figyelembe az összes ilyen szálvégrehajtási következményt.
Az egyidejűségi értékhez a legjobb általános maximális érték a számítógépen található CPU-k száma. Ha a tranzakció hosszadalmas számítást igényelt, a nagyobb egyidejűségi érték több szál futtatását teszi lehetővé. Az egyes befejező csomagok befejezése hosszabb időt vehet igénybe, de egyszerre több befejező csomagot is feldolgozunk. Kísérletezhet az egyidejűségi értékkel a profilkészítési eszközökkel együtt, hogy a lehető legjobb hatást érhesse el az alkalmazás számára.
A rendszer lehetővé teszi a GetQueuedCompletionStatus-ben várakozó szálak számára a befejező csomag feldolgozását, ha egy másik, ugyanahhoz az I/O-befejezési porthoz társított futó szál más okokból várakozási állapotba kerül, például a SuspendThread függvényt. Amikor a várakozási állapotban lévő szál újra elindul, előfordulhat, hogy egy rövid időszak, amikor az aktív szálak száma meghaladja az egyidejűségi értéket. A rendszer azonban gyorsan csökkenti ezt a számot, mivel nem engedélyezi az új aktív szálakat, amíg az aktív szálak száma nem esik az egyidejűségi érték alá. Ez az egyik oka annak, hogy az alkalmazás több szálat hoz létre a szálkészletében, mint az egyidejűségi érték. A szálkészlet kezelése meghaladja a jelen témakör hatókörét, de jó hüvelykujjszabály, hogy legalább kétszer annyi szál legyen a szálkészletben, mint amennyi processzor található a rendszeren. További információ a szálkészletezésről: Szálkészletek.
Támogatott I/O-függvények
Az alábbi függvények az I/O-befejezési portok használatával befejezett I/O-műveletek indítására használhatók. Az I/O-befejezési port mechanizmusának engedélyezéséhez át kell adnia a függvénynek az ÁTFEDÉSBEN LÉVŐ struktúrájának egy példányát és egy korábban egy I/O-befejezési porthoz társított fájlleírót (CreateIoCompletionPorthívásával):
- AcceptEx
- ConnectNamedPipe
- DeviceIoControl
- LockFileEx
- ReadDirectoryChangesW
- ReadFile
- TransactNamedPipe
- WaitCommEvent
- WriteFile
- WSASendMsg
- WSASendTo
- WSASend
- WSARecvFrom
- LPFN_WSARECVMSG (WSARecvMsg)
- WSARecv
Kapcsolódó témakörök