是C#原子的基本算术运算

时间:2012-05-10 08:39:46

标签: c# thread-safety locking arithmetic-expressions

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

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

例如

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

或应该是

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

3 个答案:

答案 0 :(得分:8)

The spec总结得非常好。第5.5节“变量引用的原子性”:

  

以下数据类型的读写是原子的:bool,char,   byte,sbyte,short,ushort,uint,int,float和reference类型。在   添加,读取和写入具有基础类型的枚举类型   以前的列表也是原子的。读写其他类型的,   包括long,ulong,double和decimal,以及用户定义的   类型,不保证是原子的。除了图书馆   为此目的而设计的功能,不保证原子   read-modify-write,例如在递增或递减的情况下。

结论:

  • 独立读/写是原子的(但仅适用于某些数据类型)
  • 读取/修改/写入(例如i++从不原子
  • 您可以使用Interlocked类方法在尚未保证的情况下实现原子性

如果Interlocked功能不够,除了使用同步原语外没有其他选择,例如Monitor.Enter(编译器也通过lock语句公开)。

答案 1 :(得分:2)

独立读写在大多数类型上都是原子的(不是更长的类型,通常是64位+)。但你想要的是阅读,改变,然后原子地写 - 这绝对是原子。

如果您需要增加一个值,则System.Threading.Interlocked类会有静态IncrementDecrement个动作。

如果您需要执行更复杂的求和,则使用lock或其他构造进行同步是唯一的方法。这或者使消息系统中的内容不可变,因此您不会遇到任何共享数据访问问题,但除非是为前期设计,否则通常无法实现。

答案 2 :(得分:0)

两者都没有...你应该选择System.Threading.Interlocked.Increment(ref int location)。这是无锁的,因为它确保处理器按照所需顺序执行读取和写入(指令 - 原子) - 而不使用System.Threading.Interlocked.Increment为处理器提供重新排序指令的机会。

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

btw - a nice read