在那些简单的情况下使用 C# 锁有用吗? [复制]
Posted
技术标签:
【中文标题】在那些简单的情况下使用 C# 锁有用吗? [复制]【英文标题】:Is it useful to use C# lock in those simple cases? [duplicate] 【发布时间】:2021-10-05 03:15:37 【问题描述】:在多线程环境中,锁定线程敏感资源很重要。我经常假设集合等是线程不安全的,这取决于 MS 文档,但简单类型也是线程敏感的吗?
让我们举个例子。锁定int属性访问是否有用,例如
public int SomeProperty
get
lock (_lock)
return _value;
或者是一个普通的吸气剂就足够了,即
public int SomeProperty => _value;
据我了解,简单的字段读取是线程安全的,但我仍然在网络和某些代码存储库中看到第一个示例。
第二个问题,单行指令中的值是顺序读取还是同时读取?换句话说,我这样做时是否需要锁定
public TimeSpan GetSomeExampleValue()
lock (_lock)
return _dateTime1 - _dateTime2;
或者我可以简单地做吗
public TimeSpan GetSomeExampleValue()
return _dateTime1 - _dateTime2;
【问题讨论】:
你可能想看看docs.microsoft.com/en-us/dotnet/api/… 对于最后一个例子,我将如何做取决于预期的用途。也就是说:_dateTime1
和_dateTime2
的更改是否会比GetSomeExamplValue
的调用更频繁,反之亦然?背景:有很多线程,您可能希望尽量减少冲突。
这篇文章可能会让你感兴趣:ericlippert.com/2014/03/12/…
几年前 MSDN 杂志上有一系列优秀的文章解决了这些问题。现在有点老了(从 2012 年开始),但仍然值得阅读:docs.microsoft.com/en-us/archive/msdn-magazine/2012/december/…
@Shay 似乎可以通过更系统地学习多线程来受益。这是一个有价值的在线资源:Threading in C#,作者 Joseph Albahari。您还可以购买纸质书。
【参考方案1】:
注意:这里的一切都是基于有效锁定; lock
与 int
(在第一个示例中为 lock (_value)
)是非常糟糕的事情,并且提供零保护;不要那样做!编译器可能已经对你大喊大叫了(你应该只锁定引用类型,否则它每次都会装箱并给你一个不同的对象,因此每次都有不同的锁定)。
据我了解,简单的字段读取是线程安全的,
比这要复杂得多。首先,您需要定义线程安全!您可能指的是三种不同的场景:
-
避免“撕裂”值 - 在写入发生时读取的不一致的单个值并且具有(通常)一半来自之前/之后值的一半,另一半来自另一个 - 创建第三个虚幻值逻辑上从未存在过;在这里,我们需要考虑“原子性”——C# 语言定义的类型始终是原子安全的,其中包括
int
和引用,但 不 包括 DateTime
;因此,int
示例在这里是安全的,但 DateTime
值不是(但是,在实践中,在 64 位进程中,您应该可以使用高达 64 位的结构,但是:这不能保证由 C# 规范 - 在 CLR 规范中提到)
避免多个值之间的不一致状态;再次考虑DateTime
值——即使我们不撕任何东西,我们也可以从更新的一侧获得_dateTime1
,从另一侧获得_dateTime2
,这意味着我们返回的TimeSpan
是一个幻像值不代表两个字段的任何一致的逻辑状态
内部 CPU 优化,例如乱序读/写,这意味着我们得到了非常奇怪的结果
现在
没有lock
,int
仍然免疫撕裂,但易受其他撕裂; DateTime
易受这三个因素的影响
与lock
,两者都完全防范所有三个
您可能还希望将不变性视为简化逻辑的一种方式,但请记住,readonly
是一个谎言 - 指南而不是实际规则。
【讨论】:
对不起,对于第一个示例,我的意思是lock (_lock)
而不是 lock (_value)
。所以你告诉我我应该使用锁来读取 int 值?
@Shay “是简单类型线程敏感”的简短回答是“是”。对于int
,使用System.Threading.Interlocked
可能比lock
更合适。
@Shay 如果不对具体场景和您要防范的内容进行大量讨论,就不可能回答这个问题。并发没有简单的答案。
AFAIK 即使 C# 不保证它,CLR 确实 保证 64 位处理器上的 64 位原子性(只要您使用 CLR 而不是其他运行时显然)
@Charlieface 确实 - ECMA 335v5 中的 12.6.6;明确的措辞以上是关于在那些简单的情况下使用 C# 锁有用吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章