4

基本算术运算线程安全吗?

例如,如果++对一个全局变量进行操作,它将从不同的线程中修改,是否需要对其进行锁定?

例如

void MyThread() // can have many running instances
{
    aGlobal++;
}

或者应该是

void MyThread()
{
    lock( lockerObj)
    {
        aGlobal++;
    }
}
4

3 回答 3

10

规范总结得很好。第 5.5 节,“变量引用的原子性”:

以下数据类型的读写是原子的:bool、char、byte、sbyte、short、ushort、uint、int、float 和引用类型。此外,上一个列表中具有基础类型的枚举类型的读取和写入也是原子的。其他类型的读取和写入,包括 long、ulong、double 和 decimal,以及用户定义的类型,不保证是原子的。除了为此目的设计的库函数之外,不能保证原子读-修改-写,例如在递增或递减的情况下。

结论:

  • 独立读/写是原子的(但仅适用于某些数据类型)
  • 读/修改/写(例如i++永远不是原子的
  • 当还不能保证原子性时,您可以使用Interlocked类方法来实现原子性

Interlocked功能不够的情况下,除了使用同步原语之外别无选择,例如Monitor.Enter(编译器也通过lock语句公开)。

于 2012-05-10T08:42:40.280 回答
2

在大多数类型(不是较长的类型,通常是 64 位以上)上,独立读取和写入是原子的。但是您想要的是原子地阅读、更改然后编写——这绝对不是原子的。

如果您需要增加一个值,则可以使用System.Threading.Interlocked具有静态IncrementDecrement动作的类。

如果您需要进行更复杂的求和,那么使用同步lock或其他构造是唯一的方法。或者使事物像在消息传递系统中一样不可变,这样您就不会遇到任何共享数据访问问题,但这通常是无法实现的,除非是预先设计的。

于 2012-05-10T08:42:40.503 回答
0

两者都不是......你应该去System.Threading.Interlocked.Increment(ref int location)代替。这是无锁的,因为它确保处理器以完全想要的顺序执行读取和写入(指令 - 是原子的) - 而不使用则为System.Threading.Interlocked.Increment处理器提供了重新排序指令的机会。

做大锤(或没有其他可能性,因为您可能正在做大量的操作),您也可以使用lock- 但即使在这些情况下,我也宁愿System.Threading.ReaderWriterLockSlim改用。

顺便说一句 -一个很好的阅读

于 2012-05-10T08:42:58.317 回答