2016-03-21 2 views
4

Возможно ли возможное состояние гонки в нижнем коде?Состояние гонки в 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

Это будет зависеть от того, как вы реализуете 'GetOutputData', поскольку это метод, который выделяет или повторно использует существующие объекты. Тот факт, что 'objData' объявлен вне' ForEach', сам по себе не означает, что существует риск одного потока, читающего объект другого потока. –

+2

@PaulHicks: Вы довольно точно описали проблему, не отрицая, что это проблема. Тот факт, что 'objData' объявлен за пределами лямбда, означает, что он является захватом, и все экземпляры лямбда будут иметь одну переменную. Так что да, конечно, существует риск одного потока, читающего объект другого потока. –

ответ

10

Да, есть возможное состояние гонки. Два потока может перемежать операторы в теле цикла следующим образом:

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

Поскольку objData объявлена ​​вне цикла, два потока разделяют ту же переменную. В результате Thread # 2 перезаписывает опорный набор objData по Thread # 1, а Thread # 2 objData добавляется к cbOutput дважды.

Чтобы предотвратить objData от общего доступа нескольких потоков, сделать его локальную переменную:

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

Или вы можете избавиться от этой переменной в целом:

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

Спасибо, Майкл. Это тоже мое точное сомнение. Просто для понимания - если бы я изменил это на Parallel.ForEach (list, po, (objInput) => {cbOutput.Add (GetOutputData (objInput))}); это могло бы иметь значение? – Vikas

+0

Да, это также устраняет проблему. Это эквивалентно моему второму предложению. –

+0

Спасибо - просто увидел, что ... Большое спасибо. – Vikas

1

Да.

(под редакцию удалить неверную и отвлекающую информацию)

И, как говорит другой ответ, вы можете сделать это: (остальные биты этого ответа предшествуют мои корректировки (спасибо Бен Войт)).

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

И это, очевидно, не имеет риска перезаписать объекты или память.

+3

Нет, это не совсем эквивалентно. –

+0

Да, это снова запутывает меня. Я отвечал, что было состояние гонки, тогда я протестировал его, и я не смог его создать, и тогда я написал этот ответ. Похоже, мне нужно будет воспроизвести его сложнее :) –

+4

К сожалению, тестирование не может доказать отсутствие состояния гонки. –

 Смежные вопросы

  • Нет связанных вопросов^_^