这里为什么要使用锁?

5
我目前正在阅读Joe Albahari的Threading in C#电子书,有时在他的示例代码中,他会在我看不到任何线程安全问题的地方使用锁。例如这里,他在写入和读取与不可变对象相关的_status字段时进行了锁定。
我理解如果ProgressStatus类是可变的,您需要在读取和写入它时加锁,因为如果一个线程在更新PercentComplete和StatusMessage字段之间被另一个线程读取状态抢占,第二个线程可能会得到这些字段的无效值对。(100%完成/“操作正在进行中…”)
但由于ProgressStatus是不可变的,不会出现这种无效状态。如果Joe删除了这两个锁,会出现什么线程安全问题?
2个回答

4
读者可能永远看不到_status更新为新值,比如所有读取都可能被合并成一个物理读取。
此外,如果引用的对象中的字段在初始化之前_status已经提交到内存中,则可能会看到部分初始化的对象。
请注意,这种锁定与被引用的对象无关,而是保护引用本身。
多线程访问变量时,如果其中一个访问是写入操作,则存在数据竞争。各种情况都有可能发生。我上面说的只是例子。

谢谢。然而,我认为你提到的这两个问题都可以通过使用“volatile”字段或内存屏障更加简洁地解决。如果我错了,请纠正我。 - dgmulf
1
是的,可以使用volatile来解决,但这更难理解。如果性能要求需要,应该使用复杂的原语。 - usr
1
在这种特定情况下使用内存屏障的一个问题是,这意味着在文章的第二部分中使用了一些直到第四部分才解释的内容。 - Jon Hanna

4
如果Joe删除了这两个锁,可能会引起线程安全问题。这可能导致“过期数据”,读取代码可能会缓存它并只看到旧值。这种使用锁的方式是非典型的,它从锁的副作用中受益:它具有暗示的内存屏障,可以防止看到旧副本。您通常会看到一个“volatile ProgressStatus _status;”但是volatile也有其问题。您是正确的,实际的读写操作在这里不需要锁(访问引用是原子的)。

谢谢 - 我怀疑一个volatile字段可能会有用。 "volatile也有它的问题" - 你能详细说明一下吗? - dgmulf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接