2016-03-21 2 views
4

Existe-t-il une condition de concurrence possible en dessous du code?Condition de course dans 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

Cela dépendra de la façon dont vous implémentez 'GetOutputData', puisque c'est la méthode qui alloue ou réutilise les objets existants. Le fait que 'objData' soit déclaré en dehors de' ForEach' ne signifie pas en soi qu'il y a un risque qu'un thread lit l'objet d'un autre thread. –

+2

@PaulHicks: Vous avez décrit le problème assez précisément, en plus de nier que c'est un problème. Le fait que 'objData' soit déclaré en dehors du lambda signifie qu'il s'agit d'une capture, et toutes les instances du lambda partageront une seule variable. Alors oui, il y a certainement un risque qu'un thread lit l'objet d'un autre thread. –

Répondre

10

Oui, il existe une condition de concurrence possible. Deux threads peuvent entrelacer les déclarations contenues dans le corps de la boucle comme suit:

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

Parce que objData est déclarée en dehors de la boucle, les deux fils partagent la même variable. Par conséquent, le thread n ° 2 remplace le jeu de référence objData par le thread n ° 1 et le thread n ° 2 objData est ajouté deux fois à cbOutput.

Pour éviter objData d'être partagée par plusieurs threads, en font une variable locale:

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

Ou vous pouvez vous débarrasser de la variable tout à fait:

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

Merci Michael. C'était aussi mon doute exact. Juste pour comprendre - si je devais changer cela à Parallel.ForEach (liste, po, (objInput) => {cbOutput.Add (GetOutputData (objInput))}); cela ferait-il une différence? – Vikas

+0

Oui, cela résout le problème aussi. C'est équivalent à ma deuxième suggestion. –

+0

Merci - juste vu que ... Merci beaucoup. – Vikas

1

Oui.

(sous la direction de supprimer des informations incorrectes et distrayant)

Et comme l'autre réponse dit, vous pouvez le faire: (bits restants de cette réponse datent d'avant mes corrections (grâce à Ben Voight)).

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)); 
    }); 
} 

Et cela n'a évidemment aucun risque d'écraser des objets ou de la mémoire.

+3

Non, ce n'est pas exactement équivalent. –

+0

Oui, il commence à me confondre à nouveau .. Je répondais qu'il y avait une condition de course, puis je l'ai testé et je ne pouvais pas le créer, puis j'ai écrit cette réponse. On dirait que je vais devoir le reproduire plus dur :) –

+4

Malheureusement, les tests ne peuvent pas prouver l'absence d'une condition de concurrence. –