Tegyük fel írok egy tcp kliens programot, TcpClient, NetworkStream és társaival. A NetworkStream.Read blokkoló módon működik. Nekem az kell, hogy bármikor tudjam abortálni a tcp csatornát. A Read ha miatta beragad az egy szál, az Thread.Abort állítja le (igaz, az Thread.Interruptot még nem próbáltam).
Lehetséges megoldásként az async BeginReadet javasolják, de azzal meg az a bajom, hogy minden kis darab olvasása után egy waithandlere kell várni, ami meg context switchet eredményez, így a nagysebességű időszakokban belassít.
Mi vajon a korrekt megoldás, ami megszakítható és gyors is?
Could you hire me? Contact me if you like what I’ve done in this article and think I can create value for your company with my skills.
LEAVE A COMMENT
6 COMMENTS
NetworkStream.DataAvailable a te barátod. Előbb mindig ránézel vele a NetworkStream-re és csak akkor olvasol (a Read-del) ha van mit, ha pedig van mit olvasni, akkor a Read azonnal visszatér és nem blokkol.
Persze, de ha nem avalilable, akkor meg pollozok. Esetleg Thread.Sleep(0)-val, de ettől meg lassú lehet a dolog.
Na ezt 100%-ban nem értem. Én egy helyen, ahol párhuzamosan olvasom és írom a tcp csatornát (2 szeparált thread-ben) ott a .DataAvailable-t használom while-ban egy thread.sleep-el különben 100-ra megy a proci :-D
Ez nagyon köbalta módszer? Azzal főznék, ami van és erre jutottam.
A while feltételében meg lehet amit akarsz.
A contextswitch eléggé erősnek tűnik nekem. Biztos az?
Megírtam, async és sync keverékkel. A napokban rövidítve kirakom ide.
Hát, bocs, csak most tévedtem ide, de ezekre a megoldásokra tényeleg az APM és a beginxxx megoldások a jók. Nem okoznak context switchet, leírom miért:
A CLR ezekre a feladatokra a Completion Port-okat használja. Röviden a Completion Port úgy működik, hogy az I/O Manager a Completion Port queue-jába tud aszinkron műveletek eredményeit bepakolni, mint pl amikor a socketről olvas valaki aszinkron, vagy file-ból aszinkron. A Completion Port-ra lehet ráállítani thread-eket, amik felébrednek akkor, amikor jött valami a queue-jába. De olyan okos ez a windows, hogy thread pool-hoz is hozzá tud párosítani egy Completion Port-ot, és akkor a thread pool thread ébred fel.
Van mégegy jó tulajdonsága a Completion Port-nak. Mint a hogy megemlítetted, egyéb módokon (pl ha az ember Completion Routinokat használ az aszinkron hívásaihoz) akkor context switchek meg egyéb csúnya dolgok történnek, de Completion Port-nál nem. Olyan módon van megcsinálva, hogy a Completion Portnál be lehet egyrészt állítani, hogy hány szálat engedjen futni maximum (akkor is, ha több fülel a completion porton). Egy egymagos gépen ezt a számot egyre érdemes rakni, egy négy magos gépen meg négyre. Ez azért jó, mert egyszerre csak egy szál foglalkozik a kész eredményekkel, nem kell őket váltogatni. Amikor ez a szál végez az egyik completion packet feldolgozásával, akkor ha van még a queueban, akkor ő fog vele foglalkozni, mindenféle context switch nélkül. Ha jön be másodpercenként ezer kis csomag, akkor amíg van ideje a threadnek (mert az oprendszer őt futtatja) addig ő fog dolgozni az összesen, context switch nélkül.
A CLR csinál pontosan egy Completion Port-ot, és egy io thread poolt, és összerendeli őket. Ha a BeginReadedbe olyan callback függvényt adtál be, ami nem blokkol, akkor a legnagyobb terhelésnél is egy szál fog minden bejövő kérést kiszolgálni. Bazi hatékony. Amikor lelövöd a socketet, amire kiadtad az olvasó (vagy író) műveletet, akkor is hívodik a rutinod, csak hibával ugrik ki belőle az EndRead. Szóval ha teljesítményigény van, akkor nem jöhet más szóba, csak APM. Található a neten cikk, hogy az APM alatt működö Completion Port mennyivel hatékonyabb, mint a többi. Egy szállal kiszolgál mindent, ha nem blokkol be a szál valami béna waitforsingleobject vagy testvére miatt. Ekkor sem történik nagy baj, indít egy másik szálat a pool-ból, és egy darabig két szál megy, de aztán az egyiket nem engedi szóhoz jutni megint.
libor, Hamurabi, amit ti csináltok, az a busy spin anti pattern. működik egyébként, de komoly szervert így nem lehet készíteni.