[C#] Threading

Pagina: 1
Acties:

  • BoomSmurf
  • Registratie: Maart 2003
  • Laatst online: 13-06-2025
Ik ben bezig mezelf wat C# aan proberen te leren. Heb ik net een boekje over uit. Mijn achtergrond ligt bij Delphi en ik merk dat de threading toch net iets anders werkt. Ik heb al verschillende dingen gezocht en veel gevonden, maar ik kom er nog steeds niet uit.

(1) Ik ben er inmiddels uit hoe je uit thread X via Invoke en een delegate code in je UI thread uit kan voeren. Maar als thread Y nou eens niet de UI thread is maar nog een andere thread, hoe doe ik dat? Je normale thread object heeft geen Invoke method. Als je met Wait/Pulse ed gaat werken komt je er uiteindelijk toch op uit dat je threads nog de hele tijd op elkaar zitten te wachten? In feite zoek ik, net als je met de UI thread kunt, naar cross-thread delegates (of naar hoe je de Invoke method toevoegd aan je eigen objecten), zodat de threads lekker hun eigen ding kunnen blijven doen, maar er toch signalering ed op kan treden zonder te hoeven pollen.

(2) tcpClient - BeginXXXX async methods. De delegate die hier aangeroepen wordt, wordt uitgevoerd in een eigen thread. Maar je moet de BeginXXXX method aan blijven roepen om data te blijven ontvangen (toch?) en communiceren met andere threads of iets met de data te doen (over het algemeen gesproken). Dan is het toch 'handiger' om de complete tcpClient code (incl connect etc) en blocking read/writes te doen in een aparte thread, of mis ik hier nu iets?

(3) Remoting - dit heb ik dus nog niet geprobeerd, maar het sprong meteen in mijn hoofd toen ik bovenstaande dingen dacht. Als je een remote object gebruikt, zijn de callback delegates die in je client (vanuit de server) uitgevoerd worden dan altijd in de thread vanuit waar je het remote object 'geinstantieerd' hebt? Of heeft de threading van het remote object hier ook nog mee te maken, of... ?

Dat is het wel even denk ik :)

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 21-02 03:42
BoomSmurf schreef op zondag 21 mei 2006 @ 22:08:
(1) Ik ben er inmiddels uit hoe je uit thread X via Invoke en een delegate code in je UI thread uit kan voeren. Maar als thread Y nou eens niet de UI thread is maar nog een andere thread, hoe doe ik dat? Je normale thread object heeft geen Invoke method.
Klopt, die zul je zelf moeten implementeren. Dat betekent ook dat je in de methode die als aparte thread uitgevoerd wordt, ondersteuning moet toevoegen om tussendoor die delegates aan te roepen.
(2) tcpClient - BeginXXXX async methods. De delegate die hier aangeroepen wordt, wordt uitgevoerd in een eigen thread. [..] Dan is het toch 'handiger' om de complete tcpClient code (incl connect etc) en blocking read/writes te doen in een aparte thread, of mis ik hier nu iets?
Dat klopt ook en heel vaak gebeurt het ook op deze manier. Het enige voordeel van de asynchrone methodes is dat je zelf geen threads hoeft te gebruiken voor simpele dingen waarvoor je ze niet nodig hebt. Je hoeft alleen maar methodes te implementeren die thread safe zijn.
(3) Remoting - dit heb ik dus nog niet geprobeerd, maar het sprong meteen in mijn hoofd toen ik bovenstaande dingen dacht. Als je een remote object gebruikt, zijn de callback delegates die in je client (vanuit de server) uitgevoerd worden dan altijd in de thread vanuit waar je het remote object 'geinstantieerd' hebt? Of heeft de threading van het remote object hier ook nog mee te maken, of... ?
Interesante vraag. De meeste RPC systemen geven geen garanties vanuit welke threads aanroepen gedaan worden. Het zou me verbazen als dit wel geldt voor .NET Remoting. Dat betekent dus gewoon dat je er vanuit zou moeten gaan dat elke remote call op de server (i.e. de plek waar een object geïmplementeerd wordt) een aparte thread oplevert. In de praktijk wordt er misschien met een threadpool gewerkt.

Misschien dat hierover op MSDN meer informatie te vinden is, hoewel ik me afvraag in hoeverre je je als programmeur hiermee bezig moet houden.

[ Voor 3% gewijzigd door Soultaker op 21-05-2006 22:48 ]


  • BoomSmurf
  • Registratie: Maart 2003
  • Laatst online: 13-06-2025
Soultaker schreef op zondag 21 mei 2006 @ 22:47:
Klopt, die zul je zelf moeten implementeren. Dat betekent ook dat je in de methode die als aparte thread uitgevoerd wordt, ondersteuning moet toevoegen om tussendoor die delegates aan te roepen.
Enig idee of voorbeeld hoe dat in z'n werk zou gaan? Welke calls zou je hiervoor gebruiken, etc?
Interesante vraag. De meeste RPC systemen geven geen garanties vanuit welke threads aanroepen gedaan worden. Het zou me verbazen als dit wel geldt voor .NET Remoting. Dat betekent dus gewoon dat je er vanuit zou moeten gaan dat elke remote call op de server (i.e. de plek waar een object geïmplementeerd wordt) een aparte thread oplevert. In de praktijk wordt er misschien met een threadpool gewerkt.

Misschien dat hierover op MSDN meer informatie te vinden is, hoewel ik me afvraag in hoeverre je je als programmeur hiermee bezig moet houden.
Ik denk dat je me hier verkeerd begrijpt. Het gaat me er niet zo zeer om hoe de server de calls uitvoert, maar hoe het bij de client 'terugkomt'. Peudo C# op de client:

code:
1
2
3
4
5
6
7
8
9
10
11
private void RemoteDataAvailable(...) {
  // Thread X? 100% zeker onafhankelijk van hoe het remote object in elkaar zit?
}

public void doSomethingRemote() {
  // Thread X!

  remoteObject obj = new remoteObject(...);
  ...
  obj.DataAvailable += new DataAvailableDelegate(RemoteDataAvailable);
}


De vraag is dus of in dit geval DataAvailable altijd aangeroepen wordt in de thread die het remote object (clientside) 'gecreeerd' heeft. Of dat dit bijvoorbeeld afhankelijk is van in welke thread de delegate aangemaakt wordt. Of dat het ook nog iets te maken heeft met hoe de threading in het object op de server in elkaar zit. Wellicht wel belangrijk om te weten. Ik zou zelf denken dat de server op zich geen weet heeft van threads aan de clientside (het gaan tenslotte uiteindelijk toch via IPC, TCP/IP, of whatever), en het dus op een bepaalde manier afhankelijk is van hoe en waar het aan de clientside aangeroepen wordt.

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 21-02 03:42
BoomSmurf schreef op maandag 22 mei 2006 @ 00:44:
Enig idee of voorbeeld hoe dat in z'n werk zou gaan? Welke calls zou je hiervoor gebruiken, etc?
Hmm, als ik onder Windows zou werken, zou ik een voorbeeld proberen te maken. Misschien kom ik hier later nog even op terug.
Ik denk dat je me hier verkeerd begrijpt. Het gaat me er niet zo zeer om hoe de server de calls uitvoert, maar hoe het bij de client 'terugkomt'.
Beetje verwarrend gebruik van de term 'client'; bij RPC systemen is het gebruikelijk om de kant die de invocation doet de 'client' te noemen en de kant die invocations afhandelt de 'server'. In de praktijk zijn beide processen vaak zowel client als server, maar wisselen die rollen per invocation.
De vraag is dus of in dit geval DataAvailable altijd aangeroepen wordt in de thread die het remote object (clientside) 'gecreeerd' heeft. Of dat dit bijvoorbeeld afhankelijk is van in welke thread de delegate aangemaakt wordt. Of dat het ook nog iets te maken heeft met hoe de threading in het object op de server in elkaar zit.
Ik heb nog nooit .NET Remoting gebruikt dus ik kan er geen uitsluitsel over geven, maar als het hetzelfde werkt als andere RPC mechanismen als CORBA of Java RMI, dan komt de request gewoon op een 'random' vrije thread binnen. Je zult dan zelf moeten synchroniseren.

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 21-02 03:42
Soultaker schreef op maandag 22 mei 2006 @ 01:28:
Hmm, als ik onder Windows zou werken, zou ik een voorbeeld proberen te maken. Misschien kom ik hier later nog even op terug.
Bij deze dus; ik heb het idee dat zoiets wel in de buurt komt bij wat die GUI thread doet:
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
using System;
using System.Collections.Generic;
using System.Threading;

public class Invokable
{
    protected class Invocation : IAsyncResult
    {
        public Delegate            d;
        public Object[]            args;
        public Object              result;
        public Exception           exception;
        public ManualResetEvent    complete;

        public Invocation(Delegate d, Object[] args)
        {
            this.d          = d;
            this.args       = args;
            this.complete   = new ManualResetEvent(false);
        }

        public bool IsCompleted {
            get { return complete.WaitOne(0, false); }
        }

        public WaitHandle AsyncWaitHandle {
            get { return complete; }
        }

        public bool CompletedSynchronously {
            // Geen idee wat hier het nut van is...
            get { return false; }
        }

        public Object AsyncState {
            // Idem...
            get { return null; }
        }
    }
    
    protected List<Invocation> pendingList;
    protected ManualResetEvent pendingEvent;

    public Invokable()
    {
        pendingList  = new List<Invocation>();
        pendingEvent = new ManualResetEvent(false);
    }

    public Object Invoke(Delegate d, params Object[] args)
    {
        return EndInvoke(BeginInvoke(d, args));
    }

    public IAsyncResult BeginInvoke(Delegate d, params Object[] args)
    {
        Invocation inv = new Invocation(d, args);
        lock(pendingList) {
            pendingList.Add(inv);
            pendingEvent.Set();
        }
        return inv;
    }

    public Object EndInvoke(IAsyncResult i)
    {
        Invocation inv = (Invocation)i;
        inv.complete.WaitOne();
        if(inv.exception == null)
            return inv.result;
        else
            throw inv.exception;
    }

    protected void CallDelegates()
    {
        List<Invocation> todo;

        lock(pendingList) {
            if(pendingList.Count == 0)
                return;

            todo = new List<Invocation>(pendingList);
            pendingList.Clear();
            pendingEvent.Reset();
        }

        foreach(Invocation inv in todo)
        {
            try {
                inv.result = inv.d.DynamicInvoke(inv.args);
            } catch(Exception e) {
                inv.exception = e;
            }
            inv.complete.Set();
        }
    }
}

Maar deze class doet uit zichzelf natuurlijk nog niets. Het wordt pas interessant als je een threadfunctie hebt die code uitvoert en tussendoor de delegates afhandelt:
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
class Example : Invokable
{
    public delegate void MyDelegate(int n);

    public void Run()
    {
        while(true)
        {
            if(pendingEvent.WaitOne(500, false))
                CallDelegates();
            else
                Console.WriteLine("Example.run() in " + Thread.CurrentThread.Name);
        }
    }

    public void Method(int n)
    {
        Console.WriteLine("Method(" + n + ") in " + Thread.CurrentThread.Name);
    }

    static void Main(string[] args)
    {
        Thread.CurrentThread.Name = "main thread";

        Example ex = new Example();
        Thread thread = new Thread(ex.Run);
        thread.Name = "background thread";
        thread.Start();

        // Demootje
        Random rand = new Random();
        for(int n = 0; n < 1000; ++n)
        {
            Thread.Sleep(rand.Next(100, 2000));
            Console.WriteLine("Invoking Method(" + n + ") from " + Thread.CurrentThread.Name);
            ex.BeginInvoke(new MyDelegate(ex.Method), n);
        }
    }
}

De truc zit 'm dus in Example.Run(), die zo af en toe tijd inruimt om die delegates af te handelen. Ik denk dat de GUI thread gewoon een standaard Windows message loop implementeert.

[ Voor 11% gewijzigd door Soultaker op 22-05-2006 03:06 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 21-02 21:21
callbacks vanuit een remote-object over de channel hebben me ook al heel wat hoofdbrekens opgeleverd:
[rml][ .NET] Remoting : SecurityExceptions bij delegates[/rml]
[rml][ .NET] Remoting en events[/rml]

https://fgheysels.github.io/


  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

afaik is clientside remoting een peulschil. Je behandelt die objecten alsof ze lokaal zijn. Remoting zorgt er dan voor dat alle calls ingepakt worden en synchroon terugkomen.

server-side moet je maken dat je objecten threadsafe zijn en eventueel de juiste delegates hebben:

heel simpel voorbeeld van een server-side business-object:

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
public class MyObject : IMyObject, MarshalByRefObject
{
   private int id;
   private object locker = new object();
   
   public MyObject() {}
   public int ID 
   { 
      get { return id; }
      set
      {
          lock (locker)
          {
            OnObjectChanging(new EventArgs());
            id = value;
            OnObjectChanged(new EventArgs());
          }
      }
    }
    public event EventHandler ObjectChanging;
    public event EventHandler ObjectChanged;
    void OnObjectChanging(EventArgs e) { if (ObjectChanging != null) ObjectChanging(this, e); }
    void OnObjectChanged(EventArgs e) { if (ObjectChanged != null) ObjectChanged(this, e); }
}


de clients zijn dan verantwoordelijk om al dan niet de laatste informatie opnieuw op te halen, de GUI even te refreshen etc...

Onderliggend wordt een event omgezet in een class met methodes. Elke handler die je eraan toevoegt is dus ook een functie-pointer (in C++ termen). In realiteit zal dit dan een functie-pointer zijn
naar een Remoting methode die alles netjes inpakt, opzendt naar de client. De client checkt zijn Remoting queue, roept de benodigde methodes aan en zendt alles terug.

Remoting zou normaal alles transparant moeten maken.

[ Voor 3% gewijzigd door H!GHGuY op 23-05-2006 08:31 ]

ASSUME makes an ASS out of U and ME


  • BoomSmurf
  • Registratie: Maart 2003
  • Laatst online: 13-06-2025
Bedankt Soultaker... Het kom er dus in feite op neer dat je een delegate queue in thread A, waar thread B af en toe een delegate in gooit, en thread A af en toe kijkt of er nieuwe entries zijn en die dan uitvoert? Ik meende al ergens gelezen te hebben dat die UI thread ook zoiets doet en die check uitvoert ergens in het messageloop/pump verhaal. Op een soortgelijke manier heb ik het nu ook opgelost, zonder delegates weliswaar, maar het komt op hetzelfde neer :)

Ik zal die links van whoami ook eens goed doorspitten, ziet er naar uit dat er nuttige informatie instaat die ik nodig ga hebben :)

Ook een mooie tip van highguy dat je serverside remote object threadsafe moet zijn, had ik zelf nog niet bedacht (maar is eigenlijk wel logisch verklaarbaar) :D

_/-\o_

[ Voor 14% gewijzigd door BoomSmurf op 22-05-2006 22:18 ]


  • Shift
  • Registratie: Augustus 2000
  • Laatst online: 09-02 15:33

Shift

[] Dual crazy []

Backgroundworker is een simpelere manier denk om te gebruiken.

  • whoami
  • Registratie: December 2000
  • Laatst online: 21-02 21:21
Waar denk je dat een backgroundworker class de TS kan helpen bij het leren / begrijpen van threading ? De background worker maakt abstractie van dat alles, dus is dat niet echt iets dat je nodig hebt bij het begrijpen van threading.

https://fgheysels.github.io/

Pagina: 1