基本算术运算线程安全吗?
例如,如果++
对一个全局变量进行操作,它将从不同的线程中修改,是否需要对其进行锁定?
例如
void MyThread() // can have many running instances
{
aGlobal++;
}
或者应该是
void MyThread()
{
lock( lockerObj)
{
aGlobal++;
}
}
基本算术运算线程安全吗?
例如,如果++
对一个全局变量进行操作,它将从不同的线程中修改,是否需要对其进行锁定?
例如
void MyThread() // can have many running instances
{
aGlobal++;
}
或者应该是
void MyThread()
{
lock( lockerObj)
{
aGlobal++;
}
}
规范总结得很好。第 5.5 节,“变量引用的原子性”:
以下数据类型的读写是原子的:bool、char、byte、sbyte、short、ushort、uint、int、float 和引用类型。此外,上一个列表中具有基础类型的枚举类型的读取和写入也是原子的。其他类型的读取和写入,包括 long、ulong、double 和 decimal,以及用户定义的类型,不保证是原子的。除了为此目的设计的库函数之外,不能保证原子读-修改-写,例如在递增或递减的情况下。
结论:
i++
)永远不是原子的Interlocked
类方法来实现原子性在Interlocked
功能不够的情况下,除了使用同步原语之外别无选择,例如Monitor.Enter
(编译器也通过lock
语句公开)。
在大多数类型(不是较长的类型,通常是 64 位以上)上,独立读取和写入是原子的。但是您想要的是原子地阅读、更改然后编写——这绝对不是原子的。
如果您需要增加一个值,则可以使用System.Threading.Interlocked
具有静态Increment
和Decrement
动作的类。
如果您需要进行更复杂的求和,那么使用同步lock
或其他构造是唯一的方法。或者使事物像在消息传递系统中一样不可变,这样您就不会遇到任何共享数据访问问题,但这通常是无法实现的,除非是预先设计的。
两者都不是......你应该去System.Threading.Interlocked.Increment(ref int location)
代替。这是无锁的,因为它确保处理器以完全想要的顺序执行读取和写入(指令 - 是原子的) - 而不使用则为System.Threading.Interlocked.Increment
处理器提供了重新排序指令的机会。
做大锤(或没有其他可能性,因为您可能正在做大量的操作),您也可以使用lock
- 但即使在这些情况下,我也宁愿System.Threading.ReaderWriterLockSlim
改用。
顺便说一句 -一个很好的阅读!