在 C# 中使用 Interlocked 获取和设置线程安全吗?
Posted
技术标签:
【中文标题】在 C# 中使用 Interlocked 获取和设置线程安全吗?【英文标题】:Is using Interlocked in C# get and set thread safe? 【发布时间】:2014-05-28 19:48:32 【问题描述】:是否可以通过在属性访问器中使用Interlocked
来获得线程安全的属性?
例子:
public class A()
private static long count;
public static long Count
get
return Interlocked.Read(ref count);
set
Interlocked.Exchange(ref count, value);
【问题讨论】:
“线程安全”是整个程序的属性,而不仅仅是其中的一个 sn-p,在您明确明确定义“线程安全”的含义之前,它也不是一个特别有意义的术语",表示在整个程序中将/不会满足哪些条件。话虽如此,我会说一般来说,看到这样的属性是代码气味。这对我来说是一个信号,有人认为某些东西会正常工作,而实际上它不会,尽管需要查看该属性的每一次使用才能确定。 关于示例代码的“线程安全”,唯一可以说的是,Count 的值是由属性集实际分配给它的值(如果您用简单的分配和返回替换联锁)。但是,正如 Servy 所说,如果您不准确定义您所谈论的“线程安全”属性,那并没有多大意义。 @MaMazav 你甚至不能这么说,真的。Interlocked.Read
有可能返回一个值,将其存储在临时文件中,调用 setter,更改值,然后让 getter 返回,因此您甚至不能说 getter 不返回一个陈旧的价值。添加的内存屏障有助于它不会作为陈旧,并防止重新排序获取和设置来自同一线程,但是当涉及多个线程时,您仍然可以由于在返回值之前将值存储在临时(隐式完成)中,因此可以做一些非常时髦的事情。
原子性和线程安全是不同的概念,你从这段代码中得到的原子性保证是非常弱的。您所拥有的只是一个保证,当您的程序在 32 位模式下运行时,您将永远不会读取部分更新的值。
@Servy 我唯一的主张是他编写的代码承诺返回值必须是过去分配给它的值(而不是部分分配,如 HansPassant 所说)。我不明白如何将值存储在临时文件中。
【参考方案1】:
当上面的例子运行时,get
和set
访问器的执行行为是线性化的。在不使用Interlocked
的情况下,get
和set
访问器的执行行为介于弱一致性和顺序一致性之间(即只保证表现出弱一致性)。
当作为 64 位进程运行时,同样可以通过标记字段 volatile
并使用简单的返回语句和赋值运算符来完成。但是,当作为 32 位进程运行时,对 volatile
64 位字段的操作不能保证是原子的,因此需要使用 Interlocked
来确保原子性。
【讨论】:
那么...这是是还是否? @HenkHolterman 这个问题在这种情况下没有意义,因此不能简单地回答是或否。从外部代码与相关属性交互的角度来看,我的回答是准确的。 @280Z28 当一个问题由于缺乏信息而无法回答时,您应该投票关闭它,而不是给出一个不完整且无益的答案。 @280Z28 至此,您已经给出了一些合理的声明,说明此代码做出了哪些保证。问题是我们不知道这些保证是否真的足以满足 OP 的目的,不知道它们是什么。结合大多数人实际上并不理解您提到的“弱一致性”和“顺序一致性”等概念的事实,读者可以根据这个答案确定他们的代码是否有效的可能性似乎......在我心中低落。【参考方案2】:它不是线程安全的。试试这个代码:
long i
get return Interlocked.Read(ref _i);
set Interlocked.Exchange(ref _i, value);
long _i;
void Main()
Parallel.ForEach(Enumerable.Range(0, 1000_000),
//new ParallelOptions MaxDegreeOfParallelism = 1,
x=>
i++;
);
i.Dump();
当您运行此代码时,答案不是 1000_000,而是稍微低一点,证明它不是线程安全的。不知道为什么会这样
【讨论】:
++ 后缀将捕获 i 的值并将其增加一。在 i 的值增加之前(在读取之后),多个线程可以安全地读取 i 的值。稍后这些不同的线程将“安全地”在 i 之上写入相同的值。以上是关于在 C# 中使用 Interlocked 获取和设置线程安全吗?的主要内容,如果未能解决你的问题,请参考以下文章
如何为 C# 中的枚举类型应用 InterLocked.Exchange?
SQL Server 中的 NEXT VALUE FOR @Sequence 的工作方式是不是与 C# 中的 Interlocked.Increment() 相同? [复制]
C#各种同步方法 lock, Monitor,Mutex, Semaphore, Interlocked, ReaderWriterLock,AutoResetEvent, ManualResetEv