2017-01-30 8 views
4

Рассмотрим этот кусок кода, который создает вокруг закрытия counter:Нужно ли использовать блокировку при закрытии?

uint counter = 0xFFFF; 
someList.AsParallel().ForEach(a => { uint tmp = ++counter }); 

(Пожалуйста, отведенных на мгновение очевидную проблему с помощью счетчика внутри параллельной Еогеасп).

Будет ли tmp оцениваться до 0x0000 или 0x1FFFF?

Мое рассуждение: для увеличения counter от 0xFFFF до 0x10000 требуется, по крайней мере, двухбайтовая команда ЦП, которая может быть прервана многопотоковой обработкой. Если это прерывается, есть вероятность, что будет обновлен только один байт counter - его можно временно установить на 0x00000 или 0x1FFFF.

Если я написал это так:

uint counter = 0xFFFF; 
someList.AsParallel().ForEach(a => { uint tmp = Interlocked.Increment(counter) }); 

...?

И если я избавлюсь от AsParallel, я полностью в безопасности?

ответ

2

Да, вам нужно Interlocked.Increment, закрытие не изменяет тот факт, что эта операция не является потокобезопасной. Какое закрытие будет заключаться в том, чтобы поднять выражение лямбда в класс отображения и повторно использовать тот же самый класс для каждой итерации, что приведет к увеличению количества потоков, увеличивающих счетчик.

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

public class C 
{ 
    [CompilerGenerated] 
    private sealed class <>c__DisplayClass0_0 
    { 
     public uint counter; 
     internal void <M>b__0(int a) 
     { 
      uint num = this.counter + 1u; 
      this.counter = num; 
     } 
    } 
    public void M() 
    { 
     C.<>c__DisplayClass0_0 <>c__DisplayClass0_ = new C.<>c__DisplayClass0_0(); 
     <>c__DisplayClass0_.counter = 65535u; 
     List<int> source = new List<int> { 
      1, 
      2, 
      3 
     }; 
     source.AsParallel<int>().ForAll(new Action<int>(<>c__DisplayClass0_.<M>b__0)); 
    } 
} 

И если я избавлюсь от AsParallel, я абсолютно безопасен?

Пока список или счетчик не мутируются во время повтора, вы должны быть в порядке. Из вашего примера невозможно узнать фактическую локальность данных, с которыми вы работаете, но при условии, что все методы являются локальными, вы будете в порядке.

0

Yup. Параллель - это просто синтаксический сахар для многопоточности. Вы по-прежнему должны быть потокобезопасными. Если вы одиночные потоки, вам, очевидно, не нужно блокировать (или быть потокобезопасными).