Toon posts:

[C#] comm. met server / multithreading*

Pagina: 1
Acties:
  • 103 views sinds 30-01-2008
  • Reageer

Verwijderd

Topicstarter
Net begonnen met C# onder Windows. Dit is mijn eerste serieuze poging tot OO, GUI en Windows programmeren.

Ik probeer een simple gebruikers-interface in elkaar te draaien die met een server kan communiceren. Het programma is een client.

Stap 1:
open een blocking socket
Stap 2:
indien succes, start een thread voor inkomende communicatie
de thread roept een functie aan die in een eindeloze loop de socket probeert te lezen / leest
Stap 3:
doe iets zinvols als er data binnenkomt

Dit werkt probleemloos (inclusief een server die stopt met luisteren); het probleem is echter dat als ik het programma stop, het nog steeds voorkomt in de proces lijst (task manager). Pas als ik de server stop, verdwijnt het proces.

Ik ben nu op zoek naar een manier om de thread te stoppen. M.i. zou dit moeten kunnen door in de form-closing event een thread.abort op te nemen. Helaas lijkt dat niet te werken. Waarom?

Ik kan waarschijnlijk een select gebruiken in de lees functie zodat de thread nooit langer dan bv een paar seconden blokkeert. Maar voor mijn gevoel is dat niet de juiste weg.

Ik heb geprobeerd de socket te sluiten (socket.shutdown en socket.close) in de form-closing event, maar dat lijkt geen goed idee te zijn. Het programma crashed (OK, in ieder geval blijft er niks draaien :) ). Als je echter single-step door het programma gaat, gebeurt dat niet, dus dat is kl....te debuggen.

Dus de hanvraag: hoe stop ik een thread.


Dit is de tweede 'poging'. In eerste instantie had ik een backgroundworker in gebruik, maar daar liep ik vast in het herstarten van de 'thread' nadat er data was binnen gekomen en verwerkt. Vandaar nu de poging tot threads.

PS op het moment geen source code bij de hand

  • EfBe
  • Registratie: Januari 2000
  • Niet online
Volgens mij is de documentatie van de Socket class in de .NET SDK vrij duidelijk...

Killen van threads is bijna altijd een no-no. Wat je moet doen is in je thread loop kijken naar een volatile variabele, bv een bool. Die zet je in een andere thread op true wanneer de lezende thread moet kappen. De loop van de thread kapt er dan mee.

Maar IMHO is dat een CPU vretende oplossing. Beter kun je de async opties van de Socket class gebruiken, waarbij je 1 thread nodig hebt op de client en gewoon een seintje krijgt wanneer er iets binnenkomt.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • sig69
  • Registratie: Mei 2002
  • Laatst online: 05:44
Geef je thread een boolean property die je controleert in je loop ("running", of "stopped" of iets dergelijks). Zodra deze boolean verandert laat je je thread gewoon een natuurlijke dood sterven:
code:
1
2
3
4
5
6
7
public void run()
{
  while(running)
  {
    // doe iets
  }
}

Dat is een vrij standaard manier om een thread te kunnen stoppen.

[ Voor 11% gewijzigd door sig69 op 18-10-2006 12:37 ]

Roomba E5 te koop


  • Alex
  • Registratie: Juli 2001
  • Laatst online: 08-02 12:48
sig69 schreef op woensdag 18 oktober 2006 @ 12:36:
Geef je thread een boolean property die je controleert in je loop ("running", of "stopped" of iets dergelijks). Zodra deze boolean verandert laat je je thread gewoon een natuurlijke dood sterven:
code:
1
2
3
4
5
6
7
public void run()
{
  while(running)
  {
    // doe iets
  }
}

Dat is een vrij standaard manier om een thread te kunnen stoppen.
Gelukkig begint de compiler dan niet te mekkeren ;).
Het lijkt me duidelijk dat dit absoluut not done is. Gebruik dan eventueel nog een timer om eea elke seconde aan te slingeren. Het bovenstaande absoluut NOT done.

Deze post is bestemd voor hen die een tegenwoordige tijd kunnen onderscheiden van een toekomstige halfvoorwaardelijke bepaalde subinverte plagiale aanvoegend intentioneel verleden tijd.
- Giphart


  • Teunis
  • Registratie: December 2001
  • Laatst online: 03-02 20:22
Verwijderd schreef op woensdag 18 oktober 2006 @ 12:10:
Ik ben nu op zoek naar een manier om de thread te stoppen. M.i. zou dit moeten kunnen door in de form-closing event een thread.abort op te nemen. Helaas lijkt dat niet te werken. Waarom?

Dus de hanvraag: hoe stop ik een thread.
optie 1 is al geven d.m.v boolean;

optie 2. de thread een naam geven zodat je wel kan aborten.
code:
1
2
3
Thread t = new Thread(...);
t.Start();
t.Abort();


en ook belangrijk is of je een blocking read gebruik
zoals: UdpClient.Receive
deze blijft daar wachten tot een bericht van de server.
als je deze killed. dan krijg je idd een foutmelding
oplossingen:
* controlleer of er wat te halen is (aantal bytes die klaar staan bij 0 niets doen dus)
* try catch (al is de bovenstaande beter).

misschien is de fout melding ook wel handing om hier te melden :)

Please nerf Rock, Paper is fine. Sincerely yours, Scissor.
GW2:Teunis.6427


  • whoami
  • Registratie: December 2000
  • Laatst online: 00:37
Het bovenstaande absoluut NOT done
waarom ?
Wat is er mis met
code:
1
2
3
4
5
6
while( running )
{
    if( running == false ) break;

   System.Thread.Sleep (5000); 
}
optie 2. de thread een naam geven zodat je wel kan aborten
Dit is absoluut not done, aangezien die Abort method een ThreadAbortException gooit en de thread gestopt worden, zonder dat je de controle hebt 'waar' die thread gestopt wordt.
Stel dat die Thread een aantal transacties uitvoert, en iedere transactie dus in z'n geheel moet voltooid worden, dan kan je met de gebakken peren zitten: je hebt niet de mogelijkheid om een transactie volledig te laten afwerken, alvorens de thread te laten stoppen.

[ Voor 59% gewijzigd door whoami op 18-10-2006 13:49 ]

https://fgheysels.github.io/


Verwijderd

Zelf doe ik het als volgt:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class MyThread
{
  private System.Threading.Thread workerThread = null;

  private Boolean terminated;

  public MyThread()
  {
    workerThread = new System.Threading.Thread(new System.Threading.ThreadStart(this.Execute));
    workerThread.Priority = System.Threading.ThreadPriority.BelowNormal;
    terminated = false;
    workerThread.Start();
  }

  private void Execute()
  {
    while(workerThread != null && workerThread.IsAlive)
    {
      lock(terminated)
      {
        if(terminated) break;
      }
      // do something
      System.Threading.Thread.Sleep( 50 );
    }
  }

  public void Terminate()
  {
    lock(terminated)
      terminated = true;
    if(workerThread != null)
    {
      if(workerThread.IsAlive)
      {
        workerThread.Join();
      }
      workerThread = null;
    }
  }
}

Verschil met de code van de andere posters is de lock op de terminated boolean i.v.m. thread-safety. Of is dit overkill?

  • whoami
  • Registratie: December 2000
  • Laatst online: 00:37
^^
Dit is zo'n beetje het 'Task' pattern.

Volgens mij is die lock op terminated overkill, aangezien er imho toch geen concurrente access kan zijn op die variable ?

https://fgheysels.github.io/


Verwijderd

whoami schreef op woensdag 18 oktober 2006 @ 15:49:
^^
Dit is zo'n beetje het 'Task' pattern.

Volgens mij is die lock op terminated overkill, aangezien er imho toch geen concurrente access kan zijn op die variable ?
Hoezo niet, want hoewel de Execute en Terminate beiden members van dezelfde class zijn, wordt de Terminate method in een andere Thread uitgevoerd. Theoretisch zou een multi-core processor dus tegelijkertijd access kunnen krijgen op de variabele.

Leuke test voor mijn Core Duo. Mocht het me lukken (zonder sleep en op normal priority) om een synchronisatie probleem te krijgen dan laat ik dat zeker even weten hier.

  • TheNameless
  • Registratie: September 2001
  • Laatst online: 07-02-2025

TheNameless

Jazzballet is vet!

Is het uitlezen van een boolean niet een atomic action?
Waardoor je geen mutex hoeft te locken?

[ Voor 27% gewijzigd door TheNameless op 18-10-2006 16:05 ]

Ducati: making mechanics out of riders since 1946


  • Gerco
  • Registratie: Mei 2000
  • Laatst online: 10-02 17:10

Gerco

Professional Newbie

TheNameless schreef op woensdag 18 oktober 2006 @ 16:04:
Is het uitlezen van een boolean niet een atomic action?
Waardoor je geen mutex hoeft te locken?
In Java is het zo dat de compiler kan bedenken dat die boolean nergens in de loop word veranderd en hem dus wegoptimized. Dan krijg je een while(true){ doe iets }. Door op de boolean te locken of deze volatile te maken geef je aan dat hij wel veranderd kan worden binnen de loop.

Ik ga er vanuit dat C# ook dergelijke mechanismes en problemen kent.

- "Als ik zou willen dat je het begreep, legde ik het wel beter uit!" | All number systems are base 10!


  • TheNameless
  • Registratie: September 2001
  • Laatst online: 07-02-2025

TheNameless

Jazzballet is vet!

Ja okay, maar dat was niet mijn vraag :)

Ik heb het even op MSDN opgezocht en het volgende blijkt:
code:
1
2
3
4
Reads and writes of the following data types are atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types. 
In addition, reads and writes of enum types with an underlying type in the previous list are also atomic.
Reads and writes of other types, including long, ulong, double, and decimal, as well as user-defined types, are not guaranteed to be atomic. 
Aside from the library functions designed for that purpose, there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement.


Hoef je daar iig niet meer aan te denken, maar Gerco kan natuurlijk gelijk hebben.

Ducati: making mechanics out of riders since 1946


Verwijderd

Aha, verklaard direct waarom ik dus geen probleem ondervond waar ik dit wel had verwacht. De lock op de bool kan dus weg, toch is het wel zo netjes om een Join() op de thread te doen, zodat je netjes wacht tot de thread daadwerkelijk afgehandeld is.

  • TheNameless
  • Registratie: September 2001
  • Laatst online: 07-02-2025

TheNameless

Jazzballet is vet!

Verwijderd schreef op woensdag 18 oktober 2006 @ 16:25:
Aha, verklaard direct waarom ik dus geen probleem ondervond waar ik dit wel had verwacht. De lock op de bool kan dus weg, toch is het wel zo netjes om een Join() op de thread te doen, zodat je netjes wacht tot de thread daadwerkelijk afgehandeld is.
Ik zou nog even goed lezen wat Gerco verteld, dit zou (misschien) ook in C# kunnen gebeuren (dit zou je op moeten zoeken in de MSDN)

Ducati: making mechanics out of riders since 1946


Verwijderd

In Java is het zo dat de compiler kan bedenken dat die boolean nergens in de loop word veranderd en hem dus wegoptimized. Dan krijg je een while(true){ doe iets }. Door op de boolean te locken of deze latile te maken geef je aan dat hij wel veranderd kan worden binnen de loop.
Maar while(!terminated) is toch niet hetzelfde als while(true) !? Immers kan een andere thread prima de waarde wijzigen, of wordt die waarde wijziging direct een aanpassing op de code gemaakt zodat een JZ bijv. omgezet wordt in een JNZ (ik weet niet of Java Byte Code en/of MSIL dergelijke instructies kennen)?

  • Gerco
  • Registratie: Mei 2000
  • Laatst online: 10-02 17:10

Gerco

Professional Newbie

Verwijderd schreef op woensdag 18 oktober 2006 @ 16:37:
Maar while(!terminated) is toch niet hetzelfde als while(true) !?
Voor jou niet nee, maar voor een optimizing compiler wel. Als jij dit schijft:
Java:
1
2
3
4
5
6
7
public void run() {
  boolean stop = false;

  while(!stop) {
    // Doe iets
  }
}


Kan de compiler (of eigenlijk JIT'er) prima besluiten om daar een while(true) van te maken. De optimalisaties zijn alleen gegarandeerd om binnen 1 thread geen verschil te maken. Meerdere threads hebben we niets mee te maken.

Of dat in C# ook opgaat weet ik niet, maar het is wel iets wat je niet zomaar mag negeren en dus even moet opzoeken.

- "Als ik zou willen dat je het begreep, legde ik het wel beter uit!" | All number systems are base 10!


  • TeeDee
  • Registratie: Februari 2001
  • Laatst online: 21:39

TeeDee

CQB 241

Gerco schreef op woensdag 18 oktober 2006 @ 17:15:
[...]


Voor jou niet nee, maar voor een optimizing compiler wel. Als jij dit schijft:
Java:
1
2
3
4
5
6
7
public void run() {
  boolean stop = false;

  while(!stop) {
    // Doe iets
  }
}


Kan de compiler (of eigenlijk JIT'er) prima besluiten om daar een while(true) van te maken. De optimalisaties zijn alleen gegarandeerd om binnen 1 thread geen verschil te maken. Meerdere threads hebben we niets mee te maken.

Of dat in C# ook opgaat weet ik niet, maar het is wel iets wat je niet zomaar mag negeren en dus even moet opzoeken.
Waarom maakt een compiler dan die beslissing? De redenatie daarachter zie ik namelijk niet. Toegegeven de inner workings van een compiler ken ik (nog) niet, maar het is wel interessant imo.

Heart..pumps blood.Has nothing to do with emotion! Bored


Verwijderd

Java:
1
2
3
4
5
6
7
public void run() {
  boolean stop = false;

  while(!stop) {
    // Doe iets
  }
}
Ja, maar dat lijkt me wel heel logisch, echter is dit niet hetgeen hier gesteld wordt, er wordt namelijk gesteld:
Java:
1
2
3
4
5
6
7
private boolean stop = false;

public void run() {
  while(!stop) {
    // Doe iets
  }
}

Het zou immers totaal geen nut hebben de terminated of stop boolean binnen de eigen methode te declareren, want dan kan geen enkel object de desbetreffende thread termineren.

  • Gerco
  • Registratie: Mei 2000
  • Laatst online: 10-02 17:10

Gerco

Professional Newbie

Verwijderd schreef op woensdag 18 oktober 2006 @ 17:30:
Ja, maar dat lijkt me wel heel logisch, echter is dit niet hetgeen hier gesteld wordt, er wordt namelijk gesteld:
Dat snap ik, maar daat gaat het niet om. Waar het om gaat is dat er in de Java standaard is vastgesteld dat een compiler optimalisaties mag maken mits deze de functionaliteit binnen 1 thread niet aantast.

Aangezien het ook in jouw geval niet mogelijk is dat de boolean stop aangepast word door code die binnen dezelfde thread loopt, is de compiler vrij om die hele check weg te optimizen. Daar kun je moeilijk over gaan doen en talloze voorbeelden aandragen waarbij het bij multithreading anders werkt, maar daar hoeft de compiler dus geen rekening mee te houden.

Of dat voor .NET ook opgaat weet ik niet, maar als je denk dat ik onzin praat, moet je eens [google=java volatile].

Overigens, zelfs als de compiler niet besluit om de check te verwijderen kan het nog fout gaan, op een multicore systeem bijvoorbeeld. Elke processor heeft dan een andere versie van stop in zijn cache en veranderingen op de ene CPU hoeven niet per se meteen naar de andere of naar main memory gecommuniceerd te worden.

[ Voor 14% gewijzigd door Gerco op 18-10-2006 17:41 ]

- "Als ik zou willen dat je het begreep, legde ik het wel beter uit!" | All number systems are base 10!


Verwijderd

Gerco schreef op woensdag 18 oktober 2006 @ 17:37:
[...]

Dat snap ik, maar daat gaat het niet om. Waar het om gaat is dat er in de Java standaard is vastgesteld dat een compiler optimalisaties mag maken mits deze de functionaliteit binnen 1 thread niet aantast.

Aangezien het ook in jouw geval niet mogelijk is dat de boolean stop aangepast word door code die binnen dezelfde thread loopt, is de compiler vrij om die hele check weg te optimizen. Daar kun je moeilijk over gaan doen en talloze voorbeelden aandragen waarbij het bij multithreading anders werkt, maar daar hoeft de compiler dus geen rekening mee te houden.

Of dat voor .NET ook opgaat weet ik niet, maar als je denk dat ik onzin praat, moet je eens [google=java volatile].
Ik heb hem even gezocht en las dat de optimalisatie waar jij het over hebt gaat over 'local variables', niet over class members zoals in mijn voorbeeld, dat is dan ook het verschil en zoals al eerder gezegd, het lijkt me niet meer dan logisch dat de compiler een local variabele en check wegoptimaliseert als deze toch niet wordt aangepast.
Overigens, zelfs als de compiler niet besluit om de check te verwijderen kan het nog fout gaan, op een multicore systeem bijvoorbeeld. Elke processor heeft dan een andere versie van stop in zijn cache en veranderingen op de ene CPU hoeven niet per se meteen naar de andere of naar main memory gecommuniceerd te worden.
Hier wordt ook voor gewaarschuwd (bijv. increments). In deze voorbeelden zal het echter niet uitmaken of de loop nog eenmaal extra wordt doorlopen aangezien het toch van te voren niet te voorspellen is hoeveel cycles er precies zullen worden doorlopen. Wil je dit voorkomen, dan pas je de lock weer toe, probleem opgelost.

Verwijderd

Topicstarter
Moet dit eerst even allemaal lezen en het juiste eruit filteren. De discussies gaan grotendeels aan mij voorbij.

Het idee met de boolean werkt volgens mij niet omdat ik een blocking socket gebruik. Dus als de bool eenmaal gelezen is, wordt de bool niet opnieuw gelezen totdat de blocking socket een keer iets ontvangt.

@EfBe
Wat is vrij duidelijk?

@Teunis
Foutmelding: Alleen de melding van Windows dat een programma een fout heeft gegenereerd (weet de exacte melding niet) en of ik een fout rapport wil sturen.

@whoami
Het aborten van de thread zal in mijn situatie geen probleem opleveren. Deze update alleen de gegevens in de gebruikers interface (en die wordt toch gesloten).


Ik ben vergeten de code mee te nemen, maar hieronder wat info.
De code is een aangepaste versie van het C# voorbeeld op http://msdn2.microsoft.co...m.net.sockets.socket.aspx Ik heb een aparte class gemaakt voor het socket gebeuren. 3 methodes (functies in mijn oude C terminologie):
socketconnect
socketwrite
socketread

Uit het hoofd de essentie van de socketread
C#:
1
2
3
4
while(true)
{
    return mysocket.Receive(buffer,buffer.Length,0);
}

Uit het hoofd de essentie van de thread functie:
C#:
1
2
3
4
while(socketread(some arguments)>0)
{
    // doe iets zinvols
}

  • TheNameless
  • Registratie: September 2001
  • Laatst online: 07-02-2025

TheNameless

Jazzballet is vet!

Okay dit is dus eigenlijk niet echt een threading probleem. :)

Als je de receive functie wilt onderbreken (dat wil je) en je wilt het zo netjes mogelijk programmeren, dan zou je toch echt a-synchrone reads moeten gaan doen (met BeginReceive() en EndReceive())

Ik heb zelf ook een dergelijk probleem gehad met een thirdparty library voor C++.
Hier kon ik alleen blocking reads doen. Gelukkig kon ik daar ook een timeout meegeven.

[ Voor 24% gewijzigd door TheNameless op 19-10-2006 10:42 ]

Ducati: making mechanics out of riders since 1946


Verwijderd

Topicstarter
Bedankt. async was eerder genoemd. Verder zie ik dat je een timeout kunt zetten voor de receive ( http://msdn2.microsoft.co...ocket.receivetimeout.aspx ).

Ik denk dat ik wel genoeg info heb om het probleem 'elegant' op te lossen.
async, select, timeout

  • Piels
  • Registratie: Maart 2001
  • Laatst online: 12-12-2025
Ik kwam dit artikel tegen over een Threadpool. Als ik het goed begrijp ben je naar zoiets op zoek.

De threadpool

Windows Phone Apps: Belstatus, Pinautomaten


Verwijderd

Topicstarter
OK, probleem opgelost. Een try/catch rondom de aanroep van de leesfunctie om de exceptions af te vangen en het programma crashed niet meer als de socket gesloten wordt. Geen verdere veranderingen waren nodig.

Mijn probleem was dat er geen 'fatal exception' melding kwam maar een, in mijn ogen, meer algemene melding. Een 'fatal exception' zou me waarschijnlijk direct op het goede spoor hebben gezet.

Iedereen dank.

PS: de socket error / exception was 10004

[ Voor 7% gewijzigd door Verwijderd op 20-10-2006 10:02 . Reden: PS toegevoegd ]

Pagina: 1