Laden...

Von einem Thread auf einen anderen zugreifen [und captured variables]

Erstellt von Turtle vor 16 Jahren Letzter Beitrag vor 10 Jahren 15.069 Views
Turtle Themenstarter:in
129 Beiträge seit 2007
vor 16 Jahren
Von einem Thread auf einen anderen zugreifen [und captured variables]

Hallo,
Ich hätte wieder eine Frage und bin mir sicher das mir jemand von euch helfen kann 🙂
Danke schon einmal für eure Hilfe.

Mein Problem ist folgendes:

Ich habe mehrere Threads laufen, jeder von ihnen hat einen Namen.
Wie kann ich von Thread A auf Thread B zugreifen (der name von Thread B ist bekannt) und dann den namen von Thread B ändern ?

mfg
Turtle

Wer fragt, ist ein Narr für fünf Minuten. Wer nicht fragt, bleibt ein Narr für immer.

1.130 Beiträge seit 2007
vor 16 Jahren

Mit der Variable vom typ Thread, die du entweder statich für den aktuellen Thread zur Verfügung hast, oder wenn du einen neuen Thread erstellst erzeugst. Die musst du nurnoch dem anderen Thread übergeben.

Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!

Turtle Themenstarter:in
129 Beiträge seit 2007
vor 16 Jahren

hmmm naja ich hab eine funktion in der der Thread erstellt wird


Thread myClientThread = new Thread(DistributeClientData);
myClientThread.Name = clientcounter.ToString();
myClientThread.IsBackground = true;
myClientThread.Start((object)myNetworkStream_server);

und dieser Thread läuft dann in der DistributeClientData Funktion und dort kann ich ja nichtmehr auf die Threadvariable zugreifen.

Gibts da irgend eine Möglichkeit auf andere Threads zuzugreifen und deren Namen zu ändern (aus dem aktuellen Thread heraus).

mfg
Turtle

Wer fragt, ist ein Narr für fünf Minuten. Wer nicht fragt, bleibt ein Narr für immer.

1.130 Beiträge seit 2007
vor 16 Jahren

Gleich 2 Möglichkeiten einem anderen Thread ein objekt zukommen zu lassen (Hier ein string, du willst ja eine Variable vom typ Thread übergeben. Das geht genauso.)


class Program
    {
        static void Main(string[] args)
        {
            Program pg = new Program();
            pg.parameter_1 = "funzt";
            Thread td = new Thread(new ParameterizedThreadStart(pg.Threadmethode));
            td.Start("parameter 2");
            Thread.Sleep(500);
            Console.ReadLine();
        }
        string parameter_1 = "funzt nich";

        void Threadmethode(object parameter_2)
        {
            Console.WriteLine(parameter_1);
            Console.WriteLine(parameter_2);
        }
    }

Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!

Turtle Themenstarter:in
129 Beiträge seit 2007
vor 16 Jahren

Ja, ich kann aber nur ein object übergeben ?! wenn ich 2 übergeben will bekomm ich einen compilerfehler.
und ich muss (wenn ich die threadvariable auch übergeben will) 2 variablen übergeben.

Wer fragt, ist ein Narr für fünf Minuten. Wer nicht fragt, bleibt ein Narr für immer.

630 Beiträge seit 2007
vor 16 Jahren

Du kannst ja object zurück zum Ursprungstyp casten.

To understand recursion you must first understand recursion

http://www.ilja-neumann.com
C# Gruppe bei last.fm

Turtle Themenstarter:in
129 Beiträge seit 2007
vor 16 Jahren

ja es geht ja auch nicht um das casten das mach ich eh schon


private void DistributeClientData(object networkstream, object threadvariable)
        {
            int threadid = Convert.ToInt32(Thread.CurrentThread.Name);
            NetworkStream myNetworkStream = (NetworkStream)networkstream;
            .
            .
            .
        }

es geht nur darum dass wenn ich 2 objects übergeben müchte eine compilerfehler bekomme.

Wer fragt, ist ein Narr für fünf Minuten. Wer nicht fragt, bleibt ein Narr für immer.

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo Turtle,

wenn du mehr Parameter übergeben willst, pack diese alle in ein Object-Array und übergib dieses als Parameter.

herbivore

Turtle Themenstarter:in
129 Beiträge seit 2007
vor 16 Jahren

Hallo herbivor,

auf die einfachsten Methoden das Problem zu lösen komm ich natürlich nicht 🙁

Nur irgendwie funktioniert es trotzdem nicht, ich glaub ich bin heute einfach zu blöd dafür .


 private void ClientHasConnected()
        {
            object[] array = new object[1];
            Thread myClientThread = new Thread(DistributeClientData);

            TcpClient myTcpClient = myTcpListener.AcceptTcpClient();
            NetworkStream myNetworkStream_server = myTcpClient.GetStream();

            myClientStreams[clientcounter++] = myNetworkStream_server;

            myClientThread.Name = clientcounter.ToString();
            myClientThread.IsBackground = true;

            array[0] = (object)myNetworkStream_server;
            array[1] = (object)myClientThread;

            myClientThread.Start(array);
        }

      private void DistributeClientData(object[] array)
        {
            int threadid = Convert.ToInt32(Thread.CurrentThread.Name);
            NetworkStream myNetworkStream = (NetworkStream)array[0];
            Thread myClientThread = (Thread)array[1];
            .
            .
            .
        }

So schaut das ganze bei mir zur zeit aus jedoch bekomm ich noch immer zwei Compilerfehler welche lauten:

Die beste Übereinstimmung für die überladene System.Threading.Thread.Thread(System.Threading.ThreadStart)-Methode hat einige ungültige Argumente.

1-Argument: kann nicht von "Methodengruppe" in "System.Threading.ThreadStart" konvertiert werden.

Irgendwie weis ich nichtmehr weiter, vieleicht ist es ja nur ne kleinigkeit aber anscheinend übersehe ich das irgendwie.

Wer fragt, ist ein Narr für fünf Minuten. Wer nicht fragt, bleibt ein Narr für immer.

564 Beiträge seit 2005
vor 16 Jahren

Versuch den Code doch einfach mal so wie floste ihn dir vorgegeben hat bevor du ihn veränderst. Und dein Array ist auch zu klein.

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo Turtle,

new Thread(new ThreadStart (DistributeClientData));

herbivore

Turtle Themenstarter:in
129 Beiträge seit 2007
vor 16 Jahren

Hallo ZiMD

Ich hatte ja schon funktionierenden Code, jedoch wurde bei diesem nur eine einzelne Variable vom Typ object übergeben.

Hallo herbivore
Ich hab trotz new Thread(new ThreadStart (DistributeClientData)); einen Fehler.

Keine Überladung für "DistributeClientData" stimmt mit dem Delegaten "System.Threading.ThreadStart" überein.

Wer fragt, ist ein Narr für fünf Minuten. Wer nicht fragt, bleibt ein Narr für immer.

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo Turtle,

die Fehlermeldung ist doch eindeutig. Siehe [Hinweis] Syntaxfehler selbst lösen (Compilerfehlermeldungen) . DistributeClientData muss Object und nicht Object [] als Parametertyp haben.

herbivore

Turtle Themenstarter:in
129 Beiträge seit 2007
vor 16 Jahren

Hallo herbivor,
danke für den Link.
Wie kann ich dann ein Object array übergeben wenn ich nur ein Object als Parametertyp haben kann?

EDIT: Ich hab das ganze jetzt einfach mit einem globalen Array gelöst.
D.h. ich habe jetzt die thread Variable in meiner Methode in welcher der Thread läuft aber wie kann ich jetzt damit auf andere Threads zugreifen und diese umbenennen?

Wer fragt, ist ein Narr für fünf Minuten. Wer nicht fragt, bleibt ein Narr für immer.

Turtle Themenstarter:in
129 Beiträge seit 2007
vor 16 Jahren

Kann mir niemand weiterhelfen?
Ich müsste dringend wissen wie ich von einem laufenden Thread den namen ändern kann (von einem anderen Thread aus).

Wer fragt, ist ein Narr für fünf Minuten. Wer nicht fragt, bleibt ein Narr für immer.

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo Turtle,

Wie kann ich dann ein Object array übergeben wenn ich nur ein Object als Parametertyp haben kann?

an einen Parameter vom Typ Object kann man beliebige Objekte, also auch ein Object-Array übergeben.

EDIT: Ich hab das ganze jetzt einfach mit einem globalen Array gelöst.

Ganz schlechte Idee. Solltest du nicht machen. Ist ja auch gar nicht nötig.

herbivore

Turtle Themenstarter:in
129 Beiträge seit 2007
vor 16 Jahren

Hallo herbivore,

Stimmt ich hab das jetzt ausprobiert nur ich werde aus der ganzen Sache nicht schlau.
Sobald ich das so mache bekomme ich den Fehler das ich nicht auf die arrayelemente bei einem object zugreifen kann.

Indizierung mit [] kann nicht auf einen Ausdruck vom Typ "object" angewendet werden.

Viel mehr würde mich allerdings mein zuerst genanntes Problem interessieren. Wie ich den Threadnamen eines Threads aus einem anderen Thread heraus ändern kann.

Wer fragt, ist ein Narr für fünf Minuten. Wer nicht fragt, bleibt ein Narr für immer.

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo Turtle,

Sobald ich das so mache bekomme ich den Fehler das ich nicht auf die arrayelemente bei einem object zugreifen kann.

das sind doch nun wirklich Grundlagen. Du musst casten. Siehe auch [Hinweis] Syntaxfehler selbst lösen (Compilerfehlermeldungen)

Wie ich den Threadnamen eines Threads aus einem anderen Thread heraus ändern kann.

Durch eine Zuweisung an Thread.Name. Du brauchst halt nur eine Referenz auf das Thread-Objekt, dessen Namen du ändern willst.

herbivore

Turtle Themenstarter:in
129 Beiträge seit 2007
vor 16 Jahren

Hallo herbivore,
ich caste ja ...


private void DistributeClientData(object array)
        {            
            int threadid = Convert.ToInt32(Thread.CurrentThread.Name);
            NetworkStream myNetworkStream = (NetworkStream)array[0];
            Thread myClientThread = (Thread)array[1]; 

Wenn es nicht das ist was du meinst dann versteh ich nicht wie es anderst gemacht gehört.

Wer fragt, ist ein Narr für fünf Minuten. Wer nicht fragt, bleibt ein Narr für immer.

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo Turtle,

du musst natürlich zuerst array auf Object [] casten, bevor du per Index zugreifen kannst. Das ist genau das, was die Fehlermeldung sagt. Sowas musst du echt selber rausbekommen lernen.

herbivore

Turtle Themenstarter:in
129 Beiträge seit 2007
vor 16 Jahren

Hallo herbivore,
vielen Dank für deine Erklärung. Mit dem selber Rausfinden hast du recht, nur ich tu mir da nicht so leicht.

Wer fragt, ist ein Narr für fünf Minuten. Wer nicht fragt, bleibt ein Narr für immer.

630 Beiträge seit 2007
vor 16 Jahren
ThreadStarter mit Codeblock? Was haltet ihr davon?

Hallo,

hier gab es das Problem dass nur eine Methode mit einem Object als Parameter an einen Thread übergeben werden kann.

Was haltet Ihr von folgendem Konstrukt?

ThreadStart starter = delegate { Methode(param1,param2,param3,...); };

Kann man das so verwenden? Wenn nein, warum nicht? Wo bestehen Gefahren und Nachteile?

Gruss
tscherno

To understand recursion you must first understand recursion

http://www.ilja-neumann.com
C# Gruppe bei last.fm

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo tscherno,

Kann man das so verwenden?

ja, gute Idee. Ist besser als der Vorschlag mit dem Objekt-Array.

Deshalb habe ich deine Beitrag auch an den anderen Thread angehängt.

herbivore

EDIT:

Die Verwendung von anonymen Delegaten zur Parameterübergabe an Threads kann, wenn man nicht genau aufpasst, fehlerhafte Ergebnisse produzieren und ist daher nur mit Einschränkungen empfehlen!

Mehr dazu weiter unten.

R
402 Beiträge seit 2005
vor 16 Jahren

hi,das verstehe ich nicht ganz,kannst du mir ein konkretes beispiel aufschreiben?? threadstart erwartet doch keinen parameter, wie kann man ihn dann mehrere übergeben??
danke
lg rizi

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo rizi,

tscherno hat doch ein Beispiel geschrieben. Damit wird, wenn man den Thread startet, die Methode Methode mit den Parametern param1,param2,param3 aufgerufen. Das ist - von den drei Punkten für evtl. mehr Parameter angesehen - gültiger C# Code.

herbivore

R
402 Beiträge seit 2005
vor 16 Jahren

das ist mir schon klar, was ich aber nicht verstehe ist: ThreadStart ist ein delegate, der eigenltich keine Parameter erlaubt und void als Rückgabewer hat,so und warum kann man dass dann umgehen und eine Mehtode angeben,die drei parameter hat??

lg rizi

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo rizi,

der ThreadStart-Delegat hat ja auch nach wie vor keine Parameter. Und er wird auch ohne Parameter aufgerufen. Es ist im Gegenteil so, dass die Methode mit den Parametern erst aus dem ThreadStart-Delegaten heraus aufgerufen wird. Und der macht sich als anonyme Methode den Umstand zu nutze, dass er direkt auf die lokalen Variablen der umgebenden Methode zugreifen kann.

herbivore

R
402 Beiträge seit 2005
vor 16 Jahren

ich kenne diese syntax absolut nicht, ich kenn zwar anonyme methoden, also zb:
ThreadStart start = delegate(){ Console.writeLine("Hallo"); };
aber in der form kenne ich dass nicht, kannst du mir vielleicht einen link geben, wo ich das nachlesen kann? ich habe jetzt in mehreren büchern unter andrem auch galileo open book nachgesehn,aber nichts derartiges gefunden!
ThreadStart start = delegate { Methode(param1,param2,param3,...); };
was genau wird hier reingespeichert? ich deneke die adresse der angegebenen Methode oder nicht?
bitte um erklärung!
danke
lg rizi

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo rizi,

ich kenne diese syntax absolut nicht, ich kenn zwar anonyme methoden, also zb:

dann kennst du die Syntax doch.

ThreadStart start = delegate(){ Console.WriteLine("Hallo"); };

ist hier das gleiche wie

ThreadStart start = delegate { Console.WriteLine("Hallo"); };

ist das gleiche wie

ThreadStart start = delegate { Console.WriteLine(strHallo); };

wenn die lokale Variabe strHallo "Hallo" enthält und das ist letztendlich genau das, was in

ThreadStart start = delegate { Methode(param1,param2,param3,...); };

passiert.

herbivore

R
402 Beiträge seit 2005
vor 16 Jahren

o.k, danke dir, jetzt habe ich es verstanden ....
lg rizi

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo tscherno, hallo zusammen,

die Verwendung von anonymen Delegaten zur Parameterübergabe an Threads kann, wenn man nicht genau aufpasst, fehlerhafte Ergebnisse produzieren und ist daher nur mit Einschränkungen empfehlen!

In der Doku (Abschnitt "Anonyme Methoden (C#-Programmierhandbuch)") wird zwar die Verwendung von anonymen Delegaten im Zusammenhang mit dem Starten von Threads als gutes Beispiel hingestellt:

Beispielsweise kann die Angabe eines Codeblocks anstelle eines Delegaten in einer Situation nützlich sein, in der das Erstellen einer Methode als unnötiger Aufwand erscheinen würde. Ein gutes Beispiel dafür wäre das Starten eines neuen Threads.

Aber das gilt nur, wenn keine Parameterübergabe in dem von dir, tscherno, vorgeschlagenen Sinne verwendet wird. Das Beispiel in der Doku verwendet denn auch nur Konstanten innerhalb des anonymen Delegaten.

Dass die Parameterübergabe schiefgehen kann/wird, zeigt folgendes Beispiel sehr deutlich:


using System;
using System.Threading;

static class App
{
   public static void Main (string [] astrArg)
   {
      ThreadStart starter;
      for (int i = 1; i < 10; ++i) {
         starter = delegate () {
            Thread.Sleep (100); // Simuliert eine Verzögerung beim Thread-Wechsel.
            M (i);
         };
         new Thread (starter).Start ();
         Thread.Sleep (30); // Simuliert den Aufwand für weiter Operationen in der Schleife
      }
   }

   public static void M (int i)
   {
      Thread.Sleep (200); // Simuliert den Aufwand für weiter Operationen vor der Ausgabe
      Console.WriteLine (i);
   }
}

Die Ausgabe ist


4
5
6
7
8
9
10
10
10

und nicht wie erwartet 1 .. 9. Wichtig ist, dass diese Fehlverhalten auch ohne das Thread.Sleep (100) in dem anonymen Delegaten auftreten kann, weil es jederzeit zu Verzögerungen beim Threadwechsel kommen kann. Das Thread.Sleep simuliert einfach eine solche Verzögerung, die jederzeit auch ohne das Thread.Sleep auftreten kann.

Der springende Punkt, weshalb die Parameterübergabe nicht funktioniert ist, dass die Variable i zwar tatsächlich in dem Moment der Erzeugung (und Zuweisung) des Delegaten-Objekts (starter = delegate ...) gespeichert wird, wie man das erwarten würde und wie das auch in der Doku steht. Wird aber die Variable i später geändert (hier in der Reinitialisierung der for-Schleife), wird auch der im Delegaten-Objekt gespeicherte Wert aktualisiert. Ich habe das untersucht: Es wird vom Compiler extra Code generiert, der diese Aktualisierung vornimmt.

Die Aktualisierung ist also wohl Absicht, obwohl mir schleierhaft ist, warum.

Editiert: Jedenfalls muss man das folgende Fazit ziehen: Es ist potentiell gefährlich, Parameter per anonymen Delegaten an Threads zu übergeben und, wenn man nicht genau weiß, was man tut, man sollte daher eher die Finger davon lassen.

Es bleibt also der übliche Weg, ParameterizedThreadStart zu verwenden und ein Object-Array, das alle Parameter enthält, an Thread.Start zu übergeben.

herbivore

PS: Das Verhalten hat sich in C# 5.0 zumindest in Bezug auf foreach-Schleifenvariablen geändert. Wird eine solche Schleifenvariable als captures variable verwendet, dann wird jetzt der Wert aus dem jeweiligen Schleifendurchlauf gecaptured. Eine foreach-Schleifenvariable gilt jetzt im Schleifenrumpf deklariert, siehe Has foreach's use of variables been changed in C# 5? und What else is new in C# 5?, Abschnitt "Using loop variables in lambdas".

Suchhilfe: 1000 Worte, captures, captured variables, closures, delegate, delegates, Delegaten, lambda expressions, Lambda-Ausdrücke.

S
8.746 Beiträge seit 2005
vor 16 Jahren

die Verwendung von anonymen Delegaten zur Parameterübergabe an Threads ist nicht zu empfehlen und kann fehlerhafte Ergebnisse produzieren!

Kann, muss aber nicht. Teste mal diesen Code:

      for (int  i = 1 ; i < 10 ; ++i )
        {
            int a = i;
            starter = delegate( )
            {
                Thread.Sleep( 100 ); // Simuliert eine Verzögerung beim Thread-Wechsel.
                M( a );
            };
            new Thread( starter ).Start( );
            Thread.Sleep( 30 ); // Simuliert den Aufwand für weiter Operationen in der Schleife
        }

Hier wird die Schleifenvariable i an die Variable a zugewiesen. Der Output ist dann wie erwartet.

Man nennt dieses Verhalten von anonymoen Delegaten "Capturing", bzw. die Variablen werden als "captured variables" bezeichnet.

http://www.yoda.arachsys.com/csharp/csharp2/delegates.html

Eben diese Variablen sind gefährlich und sollten nicht bzw. nur mit Bedacht in anonyme Delegaten gesteckt werden (da braucht man nichtmal Threads um komisches Verhalten zu produzieren). Kopiert man die Werte (bei Referenzen gibt es eh Probleme) kurz vor dem Aufruf um (Deklaration muss im Scope des Delegaten erfolgen!), dann ist das ganze thread-safe.

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo svenson,

interessant! Dass es hier um captured variables geht, war schon klar. Aber dass es hilft den Scope einzuschränken, hatte ich nicht bedacht. Im nachhinein ist es jedoch klar.

Wobei mir immer noch nicht klar ist, warum captured variables nicht so implementiert sind, dass sie nur einmalig bei ihrer Erzeugung aufzeichnet werden (also in dem Moment, in dem das Delegaten-Objekt erzeugt wird). Denn alleine der Name captured variables legt implizit nahe, dass die Aufzeichnung einmalig bei der Erzeugung der jeweiligen Variablen erfolgt. In Wirklichkeit wird eine captured variable aber bei jeder Änderung der (zugehörigen Instanz der) Urspungsvariablen aktualisiert.

herbivore

S
8.746 Beiträge seit 2005
vor 16 Jahren

Man muss halt in Expressions denken, nicht in Variablen. Und dahin zielt das Ganze ja. Und der Kontext einer Expression ist halt der Scope des Delegaten.

    static SomeAction MakeDelegate()
    {
        Random rng = new Random();
        
        return delegate { Console.WriteLine (rng.Next()); };
    }

Unter LINQ wird noch deutlicher, welch lustige Sachen man damit machen kann. Ähnelt aber an die C-Wettbewerbe an undurchschaubarem Code.

Jon Skeet: Coding Blog: Fun with captured variables

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo svenson,

es heißt aber nun mal captured variables und nicht captured expressions. Insofern finde ich es schon sinnvoll in Variablen zu denken. "Denn alleine der Name captured variables legt implizit nahe, dass die Aufzeichnung einmalig bei der Erzeugung der jeweiligen Variablen erfolgt."

herbivore

630 Beiträge seit 2007
vor 16 Jahren

Hallo,

danke für euere Bemühunhen genaueres herauszufinden. Verstehe ich es richtig dass man diese Methode verwenden kann (nur verwenden soll) wenn die Übergabeparameter sich im Scope des Delegates befinden? Hab noch nicht viel Erfahrung mit solchen Delegates, deshalb hacke ich hier mal nach. 😁

Gruss
tscherno

To understand recursion you must first understand recursion

http://www.ilja-neumann.com
C# Gruppe bei last.fm

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo tscherno,

Verstehe ich es richtig dass man diese Methode verwenden kann (nur verwenden soll) wenn die Übergabeparameter sich im Scope des Delegates befinden?

nein, so kann man das nicht sagen. Man kann sagen, dass man anonyme Delegaten zur Übergabe von Parametern an Threads dann verwenden kann, wenn die lokalen Variablen, die man dafür verwendet, im Scope des Delegaten nicht geändert werden.

Wir reden dabei über lokale Wertvariablen und über lokale Refenrenzvariablen von Objekten, die immutable sind. Denn bei anderen Referenzvariablen und bei Instanzvariablen musst man unabhängig von der Art der Übergabe sowieso aufpassen.

herbivore

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo zusammen,

schon lustig, wo einem das hier besprochene in ganz anderem Zusammenhang und viel später wieder über den Weg läuft:

.NET Quiz BLog: .NET Rätsel #1 Tricky Lambda

herbivore

49.485 Beiträge seit 2005
vor 10 Jahren

Hallo zusammen,

wie ich heute erfahren habe, hat sich das Verhalten von captured variables zumindest für foreach-Schleifenvariablen in C# 5.0 geändert. Ich habe diese neue Information direkt in dem betreffenden Beitrag in Von einem Thread auf einen anderen zugreifen [und captured variables] als PS ergänzt.

herbivore