我了解Interlocked.Increment
和lock()
的功能。但我很困惑何时使用其中一个。据我所知,Interlocked.Increment
增量共享int / long值,而lock()
意味着锁定代码区域。
例如,如果我想更新字符串值,可以使用lock()
:
lock(_object)
{
sharedString = "Hi";
}
但是,Interlocked
类无法实现这一点。
Interlocked
?答案 0 :(得分:6)
Interlocked.Increment
可以而且应该用于增加共享的int
变量。
功能上使用Interlocked.Increment
与:
lock(_object)
{
counter++;
}
但Interlocked.Increment
在性能方面要便宜得多。
答案 1 :(得分:4)
Interlocked.Increment
和相关方法依赖于硬件指令来执行单个32位或64位内存值的同步修改,确保访问相同值的多个线程不会读取/写入陈旧数据。这是必要的,因为在硬件级别,处理器具有内存值的本地/总线副本(用于性能,通常称为总线内存或cpu缓存)。
lock(){}
为一段代码执行同步,而不是单个整数值。而不依赖于硬件指令来同步对变量的访问,而不是依赖于操作系统同步原语(软件,而不是硬件)来保护内存和代码执行。
此外,lock()的使用会发出内存屏障,确保从多个CPU访问相同的变量会产生同步(非陈旧)数据。在其他必须明确执行内存屏障和防护的语言/平台中,情况并非如此。
在整数值上使用Interlocked
方法更有效,因为硬件本身支持执行必要的同步。但是这种硬件支持仅适用于本机积分,例如__int32和__int64,因为硬件没有更高级别复杂类型的概念,因此Interlocked
类型不会暴露这种高级方法。因此,您无法使用Interlocked
来同步System.String
或任何System.Object
派生类型的分配。
(即使使用较低级别的语言,也可以使用相同的硬件指令分配指向字符串值的指针,事实是在.NET中,字符串对象不表示为指针,因此它在任何“纯粹的”.NET语言中都是不可能的。我正在避免使用不安全的方法来解析指针并执行字符串值的互锁分配,如果你真的想要的话,但我不知道我觉得这确实是你所要求的,而Interlocked并不支持这一点,因为在引擎盖下需要进行GC固定,这可能会比使用lock()
更加昂贵和侵入性。)
因此,对于“引用类型”的同步修改/分配,您将需要使用同步原语(即lock(){},Monitor等)。如果您需要同步的只是一个整数值(Int32,Int64),那么使用Interlocked方法会更有效。如果有多个积分值要同步,则使用lock()语句仍然有意义,例如递增一个整数,同时递减第二个整数,其中两个整数都需要作为单个逻辑运算同步。
答案 2 :(得分:0)
如果要交换参考值,并在原子操作中返回原始值,可以使用Interlocked.Exchange
。 Interlocked.Increment
完全按照它的说法执行:增加一个数字。
但是简单地为变量分配引用值,或者任何32位值类型在.NET中都是原子的。我能想到的唯一另一种情况,即后者不成立,就是如果你创建一个压缩结构并设置属性,这将迫使编译器不要在4字节边界对齐成员(但这不是你做的事情)真的经常)。