是++;C#中的原子操作?

是++;C#中的原子操作?,c#,.net,C#,.net,我有多个线程通过“+”或“-”操作访问单个Int32变量 我们是否需要在访问它之前锁定,如下所示 lock (idleAgentsLock) { idleAgents--; //we should place it here. } 或者,如果我不执行锁定,会有什么后果?它不是“原子的”,不是多线程意义上的。然而,至少在理论上,锁可以保护操作。如果有疑问,您当然可以使用联锁。减量。否++/--不是一个原子操作,但是读取和写入整数和其他原始时间被视为一个原子

我有多个线程通过“+”或“-”操作访问单个Int32变量

我们是否需要在访问它之前锁定,如下所示

    lock (idleAgentsLock)
    {
       idleAgents--;  //we should place it here.
    }
或者,如果我不执行锁定,会有什么后果?

它不是“原子的”,不是多线程意义上的。然而,至少在理论上,锁可以保护操作。如果有疑问,您当然可以使用
联锁。减量

否++/--不是一个原子操作,但是读取和写入整数和其他原始时间被视为一个原子操作

请参阅以下链接:

你也会对这种方式感兴趣


我建议在类中使用++/--运算符的原子实现。

这取决于机器体系结构。通常情况下,编译器可能会生成一条加载指令,递增/递减该值,然后存储它。因此,其他线程可能确实读取这些操作之间的值

大多数CPU指令集都有一个特殊的原子测试集指令用于此目的。假设您不想将汇编指令嵌入到C#代码中,下一个最好的方法是使用互斥,类似于您所展示的。该机制的实现最终使用一条原子指令来实现互斥(或它使用的任何指令)

简言之:是的,你应该确保相互排斥

除了这个答案的范围之外,还有其他管理共享数据的技术,这些技术可能是适当的,也可能不是,这取决于您的情况的域逻辑。

无保护的增量/减量不是线程安全的,因此在线程之间不是原子的。(尽管它可能是“原子的”,而不是实际的IL/ML转换1。)

此LINQPad示例代码显示了不可预测的结果:

void Main()
{
    int nWorkers = 10;
    int nLoad = 200000;
    int counter = nWorkers * nLoad;
    List<Thread> threads = new List<Thread>();
    for (var i = 0; i < nWorkers; i++) {
        var th = new Thread((_) => {
            for (var j = 0; j < nLoad; j++) {
                counter--; // bad
            }
        });
        th.Start();
        threads.Add(th);
    }
    foreach (var w in threads) {
        w.Join();
    }
    counter.Dump();
}
counter--;                           // bad
Interlocked.Decrement(ref counter);  // good
lock (threads) { counter--; }        // good

1即使使用
volatile
变量,结果仍然不可预测。这似乎表明(至少在这里,当我刚刚运行它时),它也不是一个原子操作符,因为竞争线程的读/写/操作是交错的。要查看在移除可见性问题时该行为是否仍然不正确(是吗?),请添加


并将上述代码修改为使用
x.counter
而不是本地
counter
变量。

更好的问题可能是,什么使用
idleAgents
并发安全?可能重复
class x {
    public static volatile int counter;
}