[C#] Thread events en invoke

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

Onderwerpen


Acties:
  • 0 Henk 'm!

  • IceM
  • Registratie: Juni 2003
  • Laatst online: 19:29
Hallo,

Dit is mijn eerste aanraking met meerdere threads in C#. Een deel van mijn (te maken) applicatie bestaat uit een netwerk gedeelte wat luistert op een bepaalde poort en meerder clients accepteerd welke commando's kunnen sturen.

Dit gebeurt in meerdere threads: [gui] -> [listener] -> [meerdere clients].

Het te ontvangen bericht moet uiteindelijk terug naar de gui, wat opgelost wordt doormiddel van events.

Het probleem:
Control objecten mogen niet door andere threads aangepast worden.

Dit weet ik. Ik heb meerdere topics hier en artikelen op internet door gelezen. Het probleem moet opgelost worden met behulp van Invoke.

Waar ik de kluts echter kwijt raak ik waar ik die Invoke moet gebruiken, en hoe dat dan correct zou moeten. De meeste voorbeelden werken met een Sender en EventArgs. Mijn delegate functie bevat echter maar 1 argument, een string waarin het bericht komt.

Om het overzichterlijk te houden heb ik even een kleine test applicatie gemaakt:

main form:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
namespace ThreadTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Test threadTest = new Test();
            threadTest.OnTekst += new Tekst(OnTextHandler);
            threadTest.Start();
        }

        public void OnTextHandler(string message)
        {
            // Dit mag dus niet, maar hoe moet het wel?
            textBox1.Text = message;
        }
    }
}


Test:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
namespace ThreadTest
{
    public delegate void Tekst(string message); 

    class Test
    {
        public event Tekst OnTekst;

        public void Start()
        {
            Thread t = new Thread(new ThreadStart(ThrowEvent));
            t.Start();
        }

        public void ThrowEvent()
        {
            if (OnTekst != null)
                OnTekst("test");
        }
    }
}


Waar en hoe moet ik nu controleren of er geïnvoked moet worden, en hoe invoke ik vervolgens? Als test heb ik de textbox een keer als argument mee gegeven, en in de thread de textbox geïnvoked. Dit werkt wel, maar dit is niet de correcte oplossing lijkt mij. (ik wil meerdere event handlers kunnen gebruiken).

Kan iemand mij hier duidelijkheid in verschaffen? Met hulp van de gevonden artikelen/topics kom ik er helaas niet uit ...

[ Voor 0% gewijzigd door IceM op 17-07-2007 22:07 . Reden: Typo ]

...


Acties:
  • 0 Henk 'm!

  • Infinitive
  • Registratie: Maart 2001
  • Laatst online: 25-09-2023
Controls/Forms hebben een methode Invoke en een methode InvokeLater. Daar geef je de code aan mee die je wilt uitvoeren in de gui thread. Code kan je meegeven door het of in een methode te stoppen en er een delegate naar die functie te maken, of door een anonieme methode te gebruiken. De laatste methode heeft diverse voordelen.

Bijvoorbeeld, afgezien van wat syntax/notatie fouten:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void executedBySomeRandomThread()
{
    ... code ...
    string newStr = "bla";

    myControl.Invoke(new delegate()
       {
         ... code die iets met het control doet ...
         ... wordt uitgevoerd op de gui thread ...
         myControl.Text = newStr;
       } );

    ... nog meer code ...
}

[ Voor 5% gewijzigd door Infinitive op 17-07-2007 23:04 ]

putStr $ map (x -> chr $ round $ 21/2 * x^3 - 92 * x^2 + 503/2 * x - 105) [1..4]


Acties:
  • 0 Henk 'm!

  • _Thanatos_
  • Registratie: Januari 2001
  • Laatst online: 05-09 14:39

_Thanatos_

Ja, en kaal

Misschien leuk om erbij te vermelden: in die invoke moet je in elk geval niet Thread.Join gaan aanroepen, want dan kun je wachten tot je een ons weegt.
Ik heb die """"fout"""" gemaakt en .NET alle hoeken van de kamer laten zien, dus oppassen met het aanroepen van Join (wat ongetwijfeld een keer zal moeten) ;)

日本!🎌


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
_Thanatos_ schreef op dinsdag 17 juli 2007 @ 23:29:
Misschien leuk om erbij te vermelden: in die invoke moet je in elk geval niet Thread.Join gaan aanroepen, want dan kun je wachten tot je een ons weegt.
Ik heb die """"fout"""" gemaakt en .NET alle hoeken van de kamer laten zien, dus oppassen met het aanroepen van Join (wat ongetwijfeld een keer zal moeten) ;)
Ik snap dat het fout gaat, maar in je GUI wil je toch eigenlijk zo min mogelijk met Threads te maken hebben? Ik heb ieder geval in mij GUI nog nooit zo veel problemen met deadlocks gehad.

Meestal had ik in methodes van de Gui die multi threaded aangeroepen konden worden gewoon zo iets staan

C#:
1
2
3
4
5
6
7
8
9
10
public void MyGuiFunc()
{
    if( InvokeRequired )
    {
        //Invoke of BeginInvoke afhankelijk van de synchroon / asynchroon requirement
        BeginInvoke( (MethodInvoker) delegate { MyGuiFunc(); } );
        return; //Deze vooral niet vergeten ;) anders heb je nog niks op gelost
     }
    // Doe hier je ding
}

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • IceM
  • Registratie: Juni 2003
  • Laatst online: 19:29
Ik heb gauw even de code van rwb geprobeerd, dit is waar ik naar opzoek was. Ik zal ook nog even kijken naar de anonieme methoden, ziet er handig uit.

Waarom ik een Thread.Join nodig zou hebben (zeker in de GUI) zou ik eerlijk gezegd niet weten ... maargoed, het probleem is opgelost, bedankt voor de informatie :)

...


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Mijn methode gebruikt ook een anonieme method ;)

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 01:56
De methode van rwb is wel goed, maar ik check meestal bij het raisen van m'n event of de eventhandler moet ge-invoked worden:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public event MyEventHandler SomeEvent;

protected void OnSomeEvent( ... )
{
    if( SomeEvent != null )
    {
          ISynchronizeInvoke trgt = SomeEvent.Target as ISynchronizeInvoke;

          if( trgt != null && trgt.InvokeRequired )
          {
               trgt.Invoke (SomeEvent, new object[] {...});
          }
          else
          {
               SomeEvent( ... );
          }
    }
}


Het is wat meer code, en het principe is hetzelfde, maar ik vind het precies iets netter. M'n eventhandler zelf hoeft zich er dan nl. niets van aan te trekken.

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • 2xdehelft
  • Registratie: November 2004
  • Laatst online: 17-09 09:25
whoami schreef op woensdag 18 juli 2007 @ 11:23:
De methode van rwb is wel goed, maar ik check meestal bij het raisen van m'n event of de eventhandler moet ge-invoked worden:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public event MyEventHandler SomeEvent;

protected void OnSomeEvent( ... )
{
    if( SomeEvent != null )
    {
          ISynchronizeInvoke trgt = SomeEvent.Target as ISynchronizeInvoke;

          if( trgt != null && trgt.InvokeRequired )
          {
               trgt.Invoke (SomeEvent, new object[] {...});
          }
          else
          {
               SomeEvent( ... );
          }
    }
}


Het is wat meer code, en het principe is hetzelfde, maar ik vind het precies iets netter. M'n eventhandler zelf hoeft zich er dan nl. niets van aan te trekken.
De code van jou kwam ik min of meer ook vaak tegen op het internet, alleen dan met net iets meer code erbij waardoor ik de draad telkens kwijt raakte.
Wat is de "beste" manier om dit te doen?

Op deze manier heb je de Invoke check code maar 1 keer nodig, wanneer je meerdere handlers met form objecten toevoegd aan dit event zul je dus vaker de check uit moeten voeren wat resulteert in dubbele code ... ik ga er dus vanuit dat dit de meest gebruikte (en daarom "beste") methode is?

Edit:
Brr ... dit is het account van mijn collega ... moest dus een reactie van IceM zijn ;)

[ Voor 3% gewijzigd door 2xdehelft op 18-07-2007 15:27 ]

Pagina: 1