2016-03-21 10 views
4

Gibt es eine mögliche Wettlaufsituation im unteren Code?Race Zustand in Parallel.ForEach?

public void Process(List<SomeObject> list) 
{ 
    SomeDataOutput objData=null; 
    ConcurrentBag<SomeDataOutput> cbOutput = new ConcurrentBag<SomeDataOutput>(); 
    ParallelOptions po = new ParallelOptions(){MaxDegreeOfParallelism=4}; 
    Parallel.ForEach(list, po, (objInput) => 
    { 
     objData = GetOutputData(objInput);//THIS LINE IS THE ONE I AM UNSURE OF. CAN objData GET OVERWRITTEN BY MULTIPLE PARALLEL THREADS? 
     cbOutput.Add(objData); 
    }); 
} 
+0

Das hängt, wie Sie bei der Implementierung abhängen 'GetOutputData', da dies die Methode ist, die vorhandene Objekte zuweist oder wiederverwendet. Die Tatsache, dass 'objData' außerhalb von' ForEach' deklariert wird, bedeutet nicht, dass das Risiko besteht, dass ein Thread das Objekt eines anderen Threads liest. –

+2

@PaulHicks: Sie haben das Problem ziemlich genau beschrieben, abgesehen davon, dass es ein Problem ist. Die Tatsache, dass 'objData' außerhalb des Lambda angegeben wird, bedeutet, dass es sich um ein Capture handelt, und alle Instanzen des Lambda teilen sich eine einzelne Variable. Es besteht also die Gefahr, dass ein Thread das Objekt eines anderen Threads liest. –

Antwort

10

Ja, es gibt eine mögliche Wettlaufsituation. Zwei Fäden könnten die Anweisungen in dem Schleifenkörper verschachteln, wie folgt:

Thread #1        Thread #2 
================================== ================================== 
objData = GetOutputData(objInput); 
             objData = GetOutputData(objInput); 
cbOutput.Add(objData); 
             cbOutput.Add(objData); 

Da objData außerhalb der Schleife deklariert ist, die beiden Gewinde teilen sich die gleiche Variable. Als Ergebnis überschreibt Thread # 2 den objData Referenzsatz von Thread # 1, und Thread # 2 objData wird zweimal zu cbOutput hinzugefügt.

Um objData aus gemeinsam genutzt von mehreren Threads zu verhindern, ist es eine lokale Variable machen:

SomeDataOutput objData = GetOutputData(objInput); 
cbOutput.Add(objData); 

Oder Sie können ganz der Variablen loszuwerden:

cbOutput.Add(GetOutputData(objInput)); 
+0

Danke Michael. Das war genau mein Zweifel. Nur zum Verständnis - wenn ich dies zu Parallel.ForEach ändern würde (Liste, po, (objInput) => {cbOutput.Add (GetOutputData (objInput))}); würde es einen Unterschied machen? – Vikas

+0

Ja, das behebt das Problem auch. Es entspricht meinem zweiten Vorschlag. –

+0

Danke - habe das gerade gesehen ... Vielen Dank. – Vikas

1

Ja.

(Edited falsche und ablenkende Informationen zu entfernen)

Und als die andere Antwort sagt, Sie können dies tun: (verbleibenden Bits dieser Antwort auf Ben Voight meine Korrekturen (dank predate)).

public void Process(List<SomeObject> list) 
{ 
    ConcurrentBag<SomeDataOutput> cbOutput = new ConcurrentBag<SomeDataOutput>(); 
    ParallelOptions po = new ParallelOptions(){MaxDegreeOfParallelism=4}; 
    Parallel.ForEach(list, po, (objInput) => 
    { 
     cbOutput.Add(GetOutputData(objInput)); 
    }); 
} 

Und das hat offensichtlich keine Gefahr von Objekten oder Speicher zu überschreiben.

+3

Nein, es ist nicht genau gleichwertig. –

+0

Ja, es fängt an, mich wieder zu verwirren .. Ich antwortete, dass es eine Race Condition gab, dann testete ich es und ich konnte es nicht schaffen, und dann schrieb ich diese Antwort. Sieht so aus, als müsste ich es härter reproduzieren :) –

+4

Leider kann das Testen nicht beweisen, dass keine Race Condition vorliegt. –