Jump to content
Unity Insider Forum
Sign in to follow this  
Kokujou

Integer Wert wird bei Aufruf eines Threads einfach überschrieben

Recommended Posts

Yo!

Es macht mich wirklich fertig und ich habe so gar keine Ahnung was da los ist. Ich rufe einen Thread in einem for-loop auf:

for (int node = 0; node < StateTree[level].Count; node++)
{
	Thread temp = new Thread(() => BuildChildNodes(Turn, level, node));
	temp.Start();
	threads.Add(temp);
}

Der Wert ist ja am Anfang 0. Aber dann wenn ich in den Thread reingehe und mir den Wert ausgeben lasse ist er plötzlich 1!!! Und das schon am Anfang des Threads. Ich kann mir nur erklären dass aus irgendwelchen irrsinnigen Gründen die Referenz auf den Integer-Wert node übergeben wurde und wenn die for-Schleife dann weitergeht und zu 1 zählt (wo sie im 1. Durchlauf übrigens schon abbricht) verändert das dann aus irgendwelchen Gründen auch den Integer-Wert im Thread.

Aber das kann doch gar nicht sein! Was ist da los?!

Share this post


Link to post
Share on other sites

Für mich ergibt das auch keinen Sinn.. ich hoffe jemand anders kann das genauer erklären.

 

Aber ich hatte mal ein sehr ähnliches Problem, als ich mit einem for-loop versucht habe UI-Buttons ein OnClick event zuzuteilen, mit der Zählvariable als Parameter.

Da  war der übergebene Parameter auch immer der 'letzte' Wert der Zählvariable.

 

Ich hab dann einfach die Zählvariable zwischengespeichert, und die dann als Parameter übergeben.

 

also

int tmpNode = node;

Thread temp = new Thread(() => BuildChildNodes(Turn, level, tmpNode));
 

vtl. funktioniert das auch hier..

Share this post


Link to post
Share on other sites

Aktuellhab ich es so gelöst dass ich Thread.Sleep(1) aufrufe bevor die Schleife endet und man glaubts nicht aber es funktioniert. Aber das ist doch dämlich!

Share this post


Link to post
Share on other sites

Der Wert der Variable node wird ausgewertet wenn der Thread beginnt und anfängt zu arbeiten. Da wir es hier mit einem Thread zu tun haben kann dies zu einem beliebigen Zeitpunkt passieren der auf jedem Fall in der Zukunft liegt, die Reihenfolge welcher der vielen Threads nun zuerst anfängt ist dabei nicht garantiert.

 

Was hier bei dir passiert ist dass du deine for Schleife in Thread 0 durchläufst. Diese for Schleife startet Thread 1 ... n. Die Threads fangen aber erst etwas später an zu laufen. Mit dem Ergebniss dass deine Zählvariable "node" von Thread 1 schon hoch gezählt wurde. Nun kommt es dass zB. Thread 1 anfängt zu arbeiten und den aktuellen Wert von "node" ausliest. Nun wurde "node" natürlich schon hochgezählt und somit hat es nicht mehr  den von dir gewünschten Wert.

 

Die Lösung wie man dieses Problem vermeidet ist ganz einfach: Deklariere eine temporäre Variable im Scope deiner for Schleife und verwende diese temporäre Variable anstatt "node". Die temporäre Variable wird in dein Lamda gehoistet anstatt von "node" und da du diese temporäre Variable nirgendwo sonst veränderst wird diese den Wert für dieses Lamda besitzen.

Share this post


Link to post
Share on other sites

Das finde ich als Java-Entwickler echt interessant. Wenn man in Java eine Integer variable in eine Funktion übergibt, wird der Wert kopiert. In C# bekommt man anscheinend den Pointer. Oder ist das nur in diesem Spezialfall so?

Auf jeden Fall wird die Lösung von Mr 3d funktionieren.

Aber mal eine andere Frage:

Wieso willst du da was mit Threads machen? Das sieht für mich aus als würdest du mit Parallelität nur Probleme an dieser Stelle bekommen. Ich würde das eher ganz einfach alles nacheinander machen. Gerade wenn du noch Turn und Level verwendest (ich vermute mal das sind globale Variablen), und du dort irgendwas änderst, gibt es Überschneidungen im Zugriff bei den Threads.

Share this post


Link to post
Share on other sites

In C# wird auch nur der Wert kopiert, aber behind the scenes wird auch noch etwas anderes getan. Es wird eine versteckte Klasse generiert deren Instanz pro Methodenaufruf erstellt und weiterverwendet wird. In dieser Klasse werden nun alle Werte gehalten die für den Lambda Ausdruck relevant sind. In diesem Fall also die "node" Variable. Kurz und knackig:

for (int i = 0; i < 5; ++i)

  DoSomethingInANiceThread(() => Print(i));

 

 

Generiert behind the scenes folgendes:

 

internal class A

{

  public int i = 0;

}



for (A instance = new A(); instance.i < 5; ++instance.i)

  DoSomethingInANiceThread(() => Print(instance.i));

 

Mr 3D: Sorry, hatte ganz überlesen dass du sowas schon geschrieben hast.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

×
×
  • Create New...