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