Warum sollte ein Iterator (.Net) in diesem Code unzuverlässig sein?

7

Ich habe ein Beispiel, dass ich jedes Mal brechen kann, wenn ich einen Iterator benutze, aber es funktioniert gut mit einer for-Schleife. Der gesamte Code verwendet für die ausführende Methode lokale Variablen. Ich bin ratlos. Es gibt entweder eine Tatsache über Iteratoren, die mir nicht bekannt sind, oder es gibt einen "ehrlich zu gut" -Bug in .Net. Ich wette auf Ersteres. Bitte helfen.

Dieser Code funktioniert zuverlässig jedes Mal. Es durchläuft alle Elemente nacheinander (z. B. 10) und startet einen neuen Thread, wobei die Ganzzahl als Argument in einer Methode an den neuen Thread übergeben wird. Es startet 10 Fäden, einen für jeden Gegenstand. 1,2,3,4,5,6,7,8,9,10 - das funktioniert immer.

ARBEITSCODE:

%Vor%

Und dieser Code wiederholt tatsächlich Elemente. Es durchläuft alle Elemente nacheinander (z. B. 10) und startet einen neuen Thread. Es startet 10 Threads, aber nicht alle 10 ganzen Zahlen. Ich sehe es beginnen 1,2,3,3,6,7,7,8,9,10. Ich verliere Zahlen.

BUSTED CODE:

%Vor%     
Jeremy Samuel 12.02.2010, 18:01
quelle

5 Antworten

12

Das Problem basiert auf der Schließung, die für Ihren Bereich generiert wurde ...

Dasselbe Problem würde in Ihrer for-Schleife auftreten, wenn Sie es so umschreiben würden ( SCHLECHTER CODE! ):

%Vor%

Das Problem ist, wenn Sie eine Variable in einem Delegaten schließen (in Ihrem Fall dmsId ), erfolgt die Schließung in dem Bereich, in dem die Variable deklariert wird. Wenn Sie eine For- oder Foreach-Schleife verwenden, erfolgt die Schließung im Rahmen der For / Foreach-Anweisung, die eine Ebene zu hoch ist.

Durch die Einführung einer temporären Variablen innerhalb wird der Fehler in der foreach-Schleife behoben:

%Vor%

Für eine detailliertere Diskussion der Geschehnisse empfehle ich, Eric Lipperts Blogpost:" Schließen der Schleifenvariablen, die als schädlich angesehen werden ".

    
Reed Copsey 12.02.2010, 18:06
quelle
3

Dies liegt am Umfang der Variablen, die Sie in der Closure verwenden.

Eric Lippert hat ein nett Blog-Post erklärt dies im Detail, und ich denke, dass andere (Jon Skeet?) darüber auch gebloggt haben.

    
Lucero 12.02.2010 18:06
quelle
3

Wenn Sie eine anonyme Methode in Ihrem foreach ausführen, erzeugt der Compiler im Grunde eine Klasse, die auf dmsId verweist. Wenn also die Threads beginnen, zeigt jeder auf die gleiche Variable. Je nachdem, wann die Threads eingeplant sind, sehen Sie, dass Zahlen doppelt oder übersprungen werden.

In der for-Schleife erstellen Sie eine Kopie der ganzen Zahl, so dass jeder Thread seinen eigenen Wert erhält.

Es gibt einige gute Daten zu diesem Problem hier .

    
dsolimano 12.02.2010 18:06
quelle
2

Das Problem ist, dass sich Closures über Variablen und nicht über Werte schließen. Das bedeutet, dass alle Delegaten eine Referenz auf dieselbe Variable erhalten und der Wert der Variablen sich jedes Mal durch die Schleife ändert.

Das sollte es beheben:

%Vor%     
Gabe 12.02.2010 18:08
quelle
1

Im ersten Fall wird dmsId innerhalb des Bereichs der for-Schleife deklariert, jeder Delegat erfasst seine eigene "Instanz" dieser Variablen.

In der zweiten Version wird dmsId für den gesamten Bereich der foreach-Schleife deklariert. Jeder Delegat erfasst die gleiche Variable - was bedeutet, dass Sie von mehreren Threads auf die gleiche Variable zugreifen, ohne dass sie gesperrt werden - es kann zu schlechtem Zeug kommen.

    
leeeroy 12.02.2010 18:06
quelle

Tags und Links