NOVINKA! E-learningové kurzy umělé inteligence. Nyní AI za nejlepší ceny. Zjisti více:
NOVINKA – Víkendový online kurz Software tester, který tě posune dál. Zjisti, jak na to!

Diskuze: TcpClient.Read - čekání na odpověď bez zaseknutí hlavního vlákna

V předchozím kvízu, Test znalostí C# .NET online, jsme si ověřili nabyté zkušenosti z kurzu.

Aktivity
Avatar
Erik Šťastný:30.11.2016 13:43

Zdravím, jak zní nadpis...

TcpClient Client = new TcpClient("127.0.0.1", 13000);
NetworkStream Stream = Client.GetStream();
NetworkStream.Write(BufferWithHeader, 0, BufferWithHeader.Length);

a následně očekávám odpověď:

NetworkStream.Read(DataLengthInByte, 0, 4);

jaké jsou způsoby abych dosílil toho, že jakmile dojdou data co očekávám bude program pokračovat za read, nicméně mezitím nebude zaseknuté hlavní vlákno?

Četl jsem něco o ReadAsync, ale jsem nováček a vůbec tomu Async nerozumím, neexistuje jednodušší způsob? klidně ne tak efektivní :)

 
Odpovědět
30.11.2016 13:43
Avatar
Odpovídá na Erik Šťastný
Michal Štěpánek:30.11.2016 14:05

zkus se mrknout na BackgroundWorker

Nahoru Odpovědět
30.11.2016 14:05
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
Odpovídá na Michal Štěpánek
Erik Šťastný:30.11.2016 14:14

BackgroundWorker, Task, Thread, vše funguje prakticky na stejno v mém případě, ne?

Můžu tenhle zápis a čtení pustit na jiném vlákně, ale problém nastává pak v komunikaci s tím hlavním vláknem. Bych musel vytvořit nějaký buffer, kam se budou obě vlákna dívat po zprávách?

Už nebudu moc použít jednoduchý return z metody

 
Nahoru Odpovědět
30.11.2016 14:14
Avatar
Odpovídá na Michal Štěpánek
Erik Šťastný:30.11.2016 14:21

Pro úplnost uvedu konkrétní příklad:

Mám metodu:

public byte[] Login(string Username, string Password)
{
    byte[] ByteUsername = Encoding.UTF8.GetBytes(Username);
    byte[] BytePassword = Encoding.UTF8.GetBytes(Password);

    DataForSend = AppendByteArray(ByteUsername ,BytePassword)

    Stream.Write(DataForSend, 0, DataForSend.Length);

    byte[] Data = new byte[50];
    Stream.Read(Data, 0, Data.Length);

    return Data
}

Nicméně teď to funguje tak, že se celý client zamrazí dokud ty data nedostane že jo, to je jasné.

A to potřebuju předělat nicméně stále potřebuji ty Data

Editováno 30.11.2016 14:23
 
Nahoru Odpovědět
30.11.2016 14:21
Avatar
Odpovídá na Erik Šťastný
Luboš Běhounek Satik:30.11.2016 14:47

Obvykle máš nějaký thread-safe List zpráv, do kterýho ti ty vlákna obsluhující TCP komunikaci jen házej přicházející zprávy a ty si je pak zpracováváš kde potřebuješ - typicky třeba i v tom hlavním vlákně.

Odesílání se pak obvykle řeší stejně - do seznamu naházíš zprávy k odeslání a vlákno obsluhující komunikaci se tam kouká a sype ty zprávy do TCP.

Akceptované řešení
+20 Zkušeností
+2,50 Kč
Řešení problému
Nahoru Odpovědět
30.11.2016 14:47
https://www.facebook.com/peasantsandcastles/
Avatar
Odpovídá na Luboš Běhounek Satik
Erik Šťastný:30.11.2016 14:52

Bezva přesně todle řešení s tím bufferem na zprávy pro přístup z více vláken mě napadlo už dřív a nechtělo se mi to dělat, tak teďka za to zaplatím přepisováním zase :D

Díky moc! :)

Takže si udělám thread safe buffer a pak například:

pošlu request na server, nastavím si nějaký flag že čekám odpověď, když bude flag true tak to po ní budu v hlavním vlákně koukat do příchozího buferu, jakmile dojde, tak zruším flag, ano? :)

 
Nahoru Odpovědět
30.11.2016 14:52
Avatar
Odpovídá na Erik Šťastný
Luboš Běhounek Satik:30.11.2016 15:31

Ne.
Posíláš normálně jednotlivý zprávy, žádnej flag neřešíš.

To komunikační vlákno jen načte bajty a zrekonstruuje z nich zprávu, kterou máš reprezentovanou nějakou třídou a tu zprávu pak hodí do toho seznamu.

Editováno 30.11.2016 15:31
Nahoru Odpovědět
30.11.2016 15:31
https://www.facebook.com/peasantsandcastles/
Avatar
Odpovídá na Luboš Běhounek Satik
Erik Šťastný:30.11.2016 15:39

Já měl na mysli, že hlavní vlákno, které poslalo nějakou zprávu si musí někde nastavit, že má očekávat v brzké době odpověď aneb. že se při každém projetí mainloopu podívá jestli se mu už vrátila odpověď do toho seznamu nebo ne. Nebo je to chybné myšlení? :-O

Editováno 30.11.2016 15:40
 
Nahoru Odpovědět
30.11.2016 15:39
Avatar
Odpovídá na Erik Šťastný
Luboš Běhounek Satik:30.11.2016 16:27

Ne, proč bys něco takovýho dělal? :)

Prostě ti běží hlavní a komunikační vlákno, to komunikační ti hází jednotlivý zprávy do hlavního, ty tam na ně zareaguješ a hodíš to zpátky komunikačnímu.

A zprávy jsou samostatný věci, jako např. tohle je moje pozice, teď jsem vystřelil, teď chci obchodovat a odpovídáš mu zprávou tohle je pozice nepřátel, tohle jsi zasáhl, tohle je obsah obchodu... :)

Nahoru Odpovědět
30.11.2016 16:27
https://www.facebook.com/peasantsandcastles/
Avatar
Odpovídá na Luboš Běhounek Satik
Erik Šťastný:30.11.2016 16:43

No todle je mi jasné, nicméně narážím na to, že odpověď daného typu by měl hledat jen když se na ni předtím ptal, ne?

Mám takový pocit, že já narážím na postup kdy se Hlavní vlákno kouká do bufferu jestli už mu přišla odpověď na něco na co se ptalo v každém cyklu

A ty na postup, že se při každém cyklu hlavního vlákna projede celý buffer? Chápeme se?

 
Nahoru Odpovědět
30.11.2016 16:43
Avatar
Odpovídá na Luboš Běhounek Satik
Erik Šťastný:30.11.2016 17:18

Zkrátka já myslel, že se bude hlavní vlákno koukat do bufferu jen když se mu to řekne, nicméně aby se tam podívalo vždy a zpracuje vše co tam najde je asi mnohem lepší, myslel jsi to tak? :)

 
Nahoru Odpovědět
30.11.2016 17:18
Avatar
Odpovídá na Erik Šťastný
Luboš Běhounek Satik:30.11.2016 17:56

Ne, vůbec.

Máš dvě vlákna, jedno pořád poslouchá a když je co k přečtení, tak přečte bajty, vytvoří z nich instanci nějaký třídy Message a pošle ji hlavnímu vláknu ke zpracování.

V tom hlavním vlákně se jen koukáš na ty už načtený zpávy a jen pošleš odpověď, pokud je to potřeba.

Nahoru Odpovědět
30.11.2016 17:56
https://www.facebook.com/peasantsandcastles/
Avatar
lukasko.simon:30.11.2016 22:23

osobne by som mozno tento pripad riesil cez async/await, vlakna su dobra vec no volil by som to skor pri velkych enterprise projektoch kde je to fakt nevyhnutne, pretoze ladenie a synchronizacia vlakien to je smrt... Implementacia pomocou async/await je celkom jednoducha, lahsia na debug, ale to je iba moj nazor :) 100 ludi 100 chuti :)

Editováno 30.11.2016 22:24
 
Nahoru Odpovědět
30.11.2016 22:23
Avatar
Odpovídá na Luboš Běhounek Satik
Erik Šťastný:1.12.2016 8:42

Asi mám právě problém s tím pochopením "pošle hlavnímu vláknu ke zpracování", možná bych mohl zmínit že hlavní vlákno je prakticky while (true) smyčka

 
Nahoru Odpovědět
1.12.2016 8:42
Avatar
Odpovídá na lukasko.simon
Erik Šťastný:1.12.2016 8:43

To už mi radilo víc lidí a nejspíš máte pravdu, nicméně mě jako začátečnikovi to moc jednoduché nepřišlo, když jsem se to snažil na msdn nebo někde pochopit :D

 
Nahoru Odpovědět
1.12.2016 8:43
Avatar
Odpovídá na Erik Šťastný
Michal Štěpánek:1.12.2016 8:57

Právě komponenta BackgroundWorker řeší synchronizaci vláken automaticky, bez nutnosti nějakých složitých nastavování...

Nahoru Odpovědět
1.12.2016 8:57
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
Odpovídá na Erik Šťastný
Luboš Běhounek Satik:1.12.2016 8:58

Zkusím to na příkladu - chci třeba jít do obchodu a obchodovat:

Máš třídu reprezentující nějakou Message - třeba něco jako

VisitShopMessage : NetMessage
{
  int IdShop;
  int IdPlayer;
}

Pak máš nějakej thread-safe List<NetMessage> (nebo Queue - fronta) a nebo všechny přístupy k tomu listu/frontě třeba jen zamykáš přes lock
https://msdn.microsoft.com/…5kehkcz.aspx
a obě vlákna mají někde referenci na ten list.

Čtecí vlákno pořád běží a jen kouká, jestli má co číst, pokud ano, tak přečte bajty, udělá z nich instanci ty třídy VisitShopMessage a uloží do toho listu, nic víc neřeší.
Ještě případně doplní playerId podle Connection, aby nebylo možný z klienta nafejkovat, že tu zprávu poslal někdo jinej.

Hlavní vlákno se v každým loopu do toho seznamu koukne, projde všechny zprávy, zpracuje je (tady třeba bys zareagoval tak, že bys poslal zpátky hrářovi zprávu, ve který bude seznam zboží toho obchodu) a pak ty zprávy vymažeš.

Editováno 1.12.2016 8:59
Nahoru Odpovědět
1.12.2016 8:58
https://www.facebook.com/peasantsandcastles/
Avatar
Odpovídá na Michal Štěpánek
Erik Šťastný:1.12.2016 8:59

Takže BackgroundWorker jako nové vlákno ideální pro amatéra jako já abych se nemusel starat o thread safe proměnné?

 
Nahoru Odpovědět
1.12.2016 8:59
Avatar
Odpovídá na Michal Štěpánek
Luboš Běhounek Satik:1.12.2016 9:00

To je trochu zavádějící vyjádření, pořád musíš řešit všechny synchronizační problémy a udržovat to thread-safe sám :)

Nahoru Odpovědět
1.12.2016 9:00
https://www.facebook.com/peasantsandcastles/
Avatar
Odpovídá na Erik Šťastný
Michal Štěpánek:1.12.2016 9:01

někde na internetu je na použití BW video, z kterého jsem to pochopil i já a to je co říct, bo mě se to musí vysvětlovat "polopaticky"...

Editováno 1.12.2016 9:01
Nahoru Odpovědět
1.12.2016 9:01
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
Odpovídá na Luboš Běhounek Satik
Erik Šťastný:1.12.2016 9:02

No jasně :)

To jsem zmínil předtím jestli to tak myslíš,

, nicméně aby se tam podívalo vždy a zpracuje vše co tam najde je asi mnohem lepší, myslel jsi to tak? :)

Jsem z toho jelen nebo jsi to přehlídl ty? :D

 
Nahoru Odpovědět
1.12.2016 9:02
Avatar
Odpovídá na Luboš Běhounek Satik
Michal Štěpánek:1.12.2016 9:03

Ale oproti klasické práci s vlákny je toto "blbuvzdorné" a pro mě to bylo v podstatě jediné možné řešení..., protože práci s vlákny, resp. synchronizaci dat jsem dodnes nepochopil. Snažím se o to taaaak dlouho a taaak marně...

Nahoru Odpovědět
1.12.2016 9:03
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
Odpovídá na Erik Šťastný
Luboš Běhounek Satik:1.12.2016 9:05

Možná jsem špatně pochopil, co myslíš tím bufferem - já to pochopil jako buffer toho TCP připojení, kde máš ještě data v surový nezpracovaný podobě.

Pokud jsi tady tím bufferem myslel ten list/frontu, pak jsi to pochopil dobře :)

Nahoru Odpovědět
1.12.2016 9:05
https://www.facebook.com/peasantsandcastles/
Avatar
Odpovídá na Luboš Běhounek Satik
Erik Šťastný:1.12.2016 9:09

Použil jsem špatnou terminologii no, bufferem jsem měl na mysli ten List :)

Nicméně budu moc rád, když se ještě zastavíme nad tím problémem s vlákny, koukl jsem na něco ohledně toho thread safe listu a když říkáš, že samotný BW to neřeší, tak jsem koukl z rychla na něco ohledně tech zámků.

Stačí tedy teoreticky, aby se pro nějaký Read a Write toho Listu přímo do těch metod zaimplementovali ty locky nebo je za tím mnohem víc?

 
Nahoru Odpovědět
1.12.2016 9:09
Avatar
Odpovídá na Michal Štěpánek
Luboš Běhounek Satik:1.12.2016 9:11

Ne, BW jen obyčejným vláknům přidává pár událostí a udržuje si nějaký threadpool - takže nemusí pokaždý znova vytvářet nový vlákno (celkem drahá operace), ale použít nějaký starý, který už je volný.
Ale zase tuším neumí nastavovat vláknům, jestli jsou background - to ovlivňuje, kdy se aplikace zavře a kdy zůstane běžet, když ještě nějaký vlákno zůstane běžet.

Takže se obecně doporučuje BW používat na nějaký kratší operace, kterých je hodně - třeba stahování souborů v downloadmanageru apod., na vlákno, co běží celou dobu a čte z TCP připojení nějaký data ve hře bych to spíš nepoužil.

Jinak musíš dávat stejnej pozor jako když Thread použiješ přímo.

Nahoru Odpovědět
1.12.2016 9:11
https://www.facebook.com/peasantsandcastles/
Avatar
Odpovídá na Erik Šťastný
Luboš Běhounek Satik:1.12.2016 9:20

Pro začátek stačí všechny přístupy (čtení/zápis) do toho listu zalockovat.
Není to zrovna rychlá operace, ale pro začátek to neřeš, časem můžeš sehnat thread-safe frontu a nebo si to napsat sám, třeba přes Interlocked :) .

Nahoru Odpovědět
1.12.2016 9:20
https://www.facebook.com/peasantsandcastles/
Avatar
Odpovídá na Luboš Běhounek Satik
Erik Šťastný:1.12.2016 9:28

Okay, bezva díky moc! Hádám, že brzo narazím na něco dalšího tak tu asi bude bez tak za chviličku nové téma na fóru :D

 
Nahoru Odpovědět
1.12.2016 9:28
Avatar
Odpovídá na Luboš Běhounek Satik
Erik Šťastný:1.12.2016 9:38

Tak ještě jeden dotaz,

K čemu tam přesně slouží ten object u toho locku?

private Object thisLock = new Object();

lock (thisLock)
        {
        }
 
Nahoru Odpovědět
1.12.2016 9:38
Avatar
Odpovídá na Erik Šťastný
Luboš Běhounek Satik:1.12.2016 9:57

K identifikaci zámku, těch zámků může bejt najednou aktivních víc.

Viz https://msdn.microsoft.com/…5kehkcz.aspx :)

Editováno 1.12.2016 9:58
Nahoru Odpovědět
1.12.2016 9:57
https://www.facebook.com/peasantsandcastles/
Avatar
Odpovídá na Luboš Běhounek Satik
Erik Šťastný:1.12.2016 10:02

No odtud jsem to zkopíroval a nebylo mi to jasné.

Takže například kdybych potřeboval více těchhle thread safe proměných ve stejné třídě?

Já použiji pro read i write ten stejný, ne? :)

 
Nahoru Odpovědět
1.12.2016 10:02
Avatar
Odpovídá na Erik Šťastný
Luboš Běhounek Satik:1.12.2016 10:43

Ono to neudělá proměnnou thread-safe, jen kód uvnitř dvou zámků zamčených stejným objektem nemůže běžet najednou.

Pokud by k tomu mělo dojít, tak ten druhý (nebo i třetí, čtvrtý, ...) počká, až to ten předchozí uvolní.

Takže lock zamyká kód, ne proměnnou, tím objectem jen určuješ identifikátor toho zámku.

Nahoru Odpovědět
1.12.2016 10:43
https://www.facebook.com/peasantsandcastles/
Avatar
Odpovídá na Luboš Běhounek Satik
Erik Šťastný:1.12.2016 11:11

Opět špatná terminologie pardon, z mého pohledu se to tak chová, že pomocí těch dvou metod čtení a zápis ji udělám prakticky thread safe z toho pohledu pokud budu používat jen ty dvě zalockované metody.

a tím identifikátorem hádám jde o to, že read i write z tohodle listu mají stejný identifikátor, kdybych vytvářel lock pro čtení nebo zápis na jinou proměnou můžu použít jiný identifikátor, správně? :)

Editováno 1.12.2016 11:12
 
Nahoru Odpovědět
1.12.2016 11:11
Avatar
Odpovídá na Erik Šťastný
Luboš Běhounek Satik:1.12.2016 11:25

J, pokud si jseš jistej, že nikde jinde než v těch dvou místech se s k té proměnné přistupovat nebude, tak je to ok. Jen bacha, aby si někdo referenci na ten List třeba někde nezkopíroval.

Ano, když použiješ jiný id, tak ta sekce s jiným id bude nezávislá na těch s prvním id. :)

Nahoru Odpovědět
1.12.2016 11:25
https://www.facebook.com/peasantsandcastles/
Avatar
Odpovídá na Luboš Běhounek Satik
Erik Šťastný:1.12.2016 11:32

List je private a ve finále to bude buildnuté DLL, tak by v tom snad neměl být problém :)

Ještě taková maličkost, hádám že do těch locků by se mělo dávat co nejmíň kódů aby to na co nejkratší dobu zbrzdilo ostatní vlákna, že?

Tzn. v případě nějakých náročnějších operací s tím Listem je možná lepší ho v tom locku jen zkopírovat do nové dočasné proměnné a pracovat s ní?

Editováno 1.12.2016 11:34
 
Nahoru Odpovědět
1.12.2016 11:32
Avatar
Nahoru Odpovědět
1.12.2016 11:35
https://www.facebook.com/peasantsandcastles/
Avatar
Odpovídá na Luboš Běhounek Satik
Erik Šťastný:1.12.2016 11:40

Bezva, ještě jednou díky moc a hádám, že se brzy potkáme v dalším vlákně, pokud se ti bude chtít pomáhat ! :D

 
Nahoru Odpovědět
1.12.2016 11:40
Avatar
Odpovídá na Erik Šťastný
Luboš Běhounek Satik:1.12.2016 11:58

Pokud o něj zakopnu, tak klidně :)

Nahoru Odpovědět
1.12.2016 11:58
https://www.facebook.com/peasantsandcastles/
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 37 zpráv z 37.