Soci (Soczó Zsolt) szakmai blogja

2010.07.02.

Blocking read kiváltása mivel?

Filed under: .NET,Szakmai élet — Soczó Zsolt @ 17:44

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?

6 Comments

  1. 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.

    Comment by libor — 2010.07.03. @ 07:32

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

    Comment by Soczó Zsolt — 2010.07.03. @ 09:35

  3. 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?

    Comment by Hamurabi — 2010.07.05. @ 08:41

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

    Comment by Zsolt Soczo — 2010.07.09. @ 21:57

  5. 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.

    Comment by Tóth Viktor — 2010.11.26. @ 14:01

  6. 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.

    Comment by Tóth Viktor — 2010.11.26. @ 14:05

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress