为什么需要锁?

use*_*000 3 .net c# parallel-processing

我正在看一个关于p的例子.斯蒂芬克莱里的40本书就是这本书

// Note: this is not the most efficient implementation. 
// This is just an example of using a lock to protect shared state. 
static int ParallelSum(IEnumerable<int> values)
{
    object mutex = new object();
    int result = 0;
    Parallel.ForEach(source: values, 
       localInit: () => 0, 
       body: (item, state, localValue) => localValue + item, 
       localFinally: localValue => 
       {
            lock (mutex)
                result += localValue;
       });
   return result;
}
Run Code Online (Sandbox Code Playgroud)

我有点困惑为什么lock需要它.因为如果我们所做的只是总结ints 的集合{1, 5, 6},那么我们就不需要关心result以任何顺序递增的共享和.

(1 + 5) + 6 = 1 + (5 + 6) = (1 + 6) + 5 = ...
Run Code Online (Sandbox Code Playgroud)

有人能解释我的思想在哪里有缺陷吗?

我想我的身体方法有点困惑不能简单

int result = 0;
Parallel.ForReach(values, (val) => { result += val; }); 
return result;
Run Code Online (Sandbox Code Playgroud)

Han*_*ney 10

添加等操作不是原子操作,因此不是线程安全的.在示例代码中,如果省略了锁,则完全有可能在几乎同时执行两个加法运算,导致其中一个值被覆盖或错误地添加.有一个方法线程安全的递增整数:Interlocked.Add(int, int).但是,由于没有使用,因此在示例代码中需要锁定以确保一次完成一个非原子加法操作(按顺序而不是并行).

  • @ M.kazemAkhgary也不太对劲.赋值和加法都是独立的原子.什么*不是*atomic是增量,它由读,加,然后赋值组成.每三个操作都是100%原子,但*所有三个*都不是原子的. (6认同)

Ole*_*nov 7

它不是关于'结果'更新的顺序,它更新它的竞争条件,记住运算符+=不是原子的,所以两个线程可能在它们触摸它之前看不到另一个线程的udpate

  • @ user7127000这不是*bug*,它是一个必不可少的功能.如果不允许CPU重新排序操作,那么您的计算机将比数百倍慢几百倍.那些许多数量级的速度的成本是线程之间的共享状态*非常*难以处理,以至于您应该不惜一切代价避免这样做,包括在您的示例中,因为您的代码会很多如果在单个线程上运行速度更快,而不是几个. (2认同)