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.

July 2, 2010 / by Zsolt Soczó

Blocking read kiváltása mivel?

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

  • libor July 3, 2010

    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.

  • Soczó Zsolt July 3, 2010

    Persze, de ha nem avalilable, akkor meg pollozok. Esetleg Thread.Sleep(0)-val, de ettől meg lassú lehet a dolog.

  • Hamurabi July 5, 2010

    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?

  • Zsolt Soczo July 9, 2010

    Megírtam, async és sync keverékkel. A napokban rövidítve kirakom ide.

  • Tóth Viktor November 26, 2010

    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.

  • Tóth Viktor November 26, 2010

    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.