我刚刚发现了这Interlocked
门课,现在我有一些基本问题。
据我了解,在多线程处理数字变量时,我应该使用 Interlocked。如果该陈述是正确的,那么进行读取或只是一般使用变量呢?
例如:
if ((iCount % 100) == 0)
我需要在那里使用Interlocked
声明吗?
当我初始化变量时怎么办:
Int32 iCount = 0;
在实施它之前,我需要确保我理解这一点。
我刚刚发现了这Interlocked
门课,现在我有一些基本问题。
据我了解,在多线程处理数字变量时,我应该使用 Interlocked。如果该陈述是正确的,那么进行读取或只是一般使用变量呢?
例如:
if ((iCount % 100) == 0)
我需要在那里使用Interlocked
声明吗?
当我初始化变量时怎么办:
Int32 iCount = 0;
在实施它之前,我需要确保我理解这一点。
这里有各种因素,但主要是波动性和原子性。在你的声明中:
if ((iCount % 100) == 0)
我需要在那里使用互锁语句吗?
我们首先需要问“什么是iCount
?”。如果是long
/ ulong
,则不能保证它是atomic,因此您绝对需要某种保护(例如 via Interlocked
)以避免获得“撕裂”值(在更新的中途读取它,给出一个幻像值它实际上从未如此 - 例如,从 更改00000000
为FFFFFFFF
您可以阅读0000FFFF
或FFFF0000
)。如果它是一个int
,它将被保证是原子的。下一个问题是:我需要查看更新吗?CPU 内置了各种级别的缓存,看起来从字段读取的代码实际上可能最终只是从本地寄存器或缓存中读取 - 而从不接触实际内存。如果这是一个风险,那么您可以通过使用来减轻这种风险,Interlocked
尽管在许多情况下 usingvolatile
也可以防止这种情况发生。
讨论更新时出现的第三个也是更棘手的问题:你想要“最后一次编辑盲目获胜”吗?如果是这样,只需更新值(可能volatile
用于允许读取) - 但是 - 如果两个线程正在编辑,则存在丢失更新的风险。例如,如果两个线程同时递增和递减,则最终值可能是0
或1
- 不一定是您想要的。Intelocked
提供使用变更检测进行更新的方法,以及执行常见操作(如递增/递减/添加/等)的辅助方法。
关于你的另一个问题:
当我初始化变量时怎么办:
Int32 iCount = 0;
字段初始化器只在一个线程上执行,所以不需要额外的保护——这很好。
然而!线程很难。如果您完全不确定,请保持简单:使用lock
. 例如(假设您想要每个实例同步):
private int iCount = 0;
private readonly object syncLock = new object();
...
lock(syncLock) {
// code that reads or manipulates iCount
}
在许多情况下,这可以正常工作。
在共享可变状态上执行多线程时,您需要同步。您不需要使用Interlocked
. Interlocked
适用于高级用户。我建议您使用lock
C# 语句,并且仅Interlocked
用于简单的情况(增加共享计数器)或性能关键情况。
Interlocked
一次只能访问一个变量,并且只支持非常原始的操作。您将很难将多个变量与Interlocked
.
在您的示例中,没有人知道您是否需要同步,因为线程安全是整个程序的属性,而不是单个语句或函数的属性。您需要将所有在共享状态上运行的代码视为一个整体。
我想补充一下微软还引入了 ImmutableInterlocked 类的其他答案。
这个类是为处理不可变集合而设计的。该类具有一组使用比较和交换模式更新不可变集合的函数。
您可以在 System.Collections.Immutable 命名空间中找到它。