C# 这里为什么用锁?

C# 这里为什么用锁?,c#,.net,multithreading,thread-safety,locking,C#,.net,Multithreading,Thread Safety,Locking,我目前正在阅读Joe Albahari的电子书,有时在他的示例代码中,他在我看不到任何线程安全问题的地方使用锁,例如,他锁定了对_status字段的写入和读取,该字段引用了一个不可变的对象 我理解,如果ProgressStatus类是可变的,您需要锁定对它的读取和写入,因为如果一个线程在更新PercentComplete和StatusMessage字段之间被另一个读取状态的线程抢占,那么第二个线程可能会获得这些字段的无效值对。(100%完成/“正在进行的操作…”) 但由于ProgressStat

我目前正在阅读Joe Albahari的电子书,有时在他的示例代码中,他在我看不到任何线程安全问题的地方使用锁,例如,他锁定了对_status字段的写入和读取,该字段引用了一个不可变的对象

我理解,如果ProgressStatus类是可变的,您需要锁定对它的读取和写入,因为如果一个线程在更新PercentComplete和StatusMessage字段之间被另一个读取状态的线程抢占,那么第二个线程可能会获得这些字段的无效值对。(100%完成/“正在进行的操作…”)


但由于ProgressStatus是不可变的,因此不会出现这种无效状态。如果Joe同时删除了这两个锁,那么会出现什么线程安全问题呢?

例如,读者可能永远看不到
\u状态更新为新值。所有读取可能会折叠为一个物理读取

此外,我认为如果在引用对象中的字段之前,将
\u status
提交到内存中,您可能会看到部分初始化的对象

请注意,此锁定与所引用的对象无关。这是关于保护引用本身的

当其中一个访问是写入时,在多个线程上访问变量是一种数据竞争。各种事情都可能发生。我上面说的只是一些例子

如果Joe移除了这两个锁,会出现什么线程安全问题

它可能会导致“过时数据”,读取代码可能会缓存它,并且只能看到旧值

lock
的这种用法是典型的,它得益于
lock
的副作用:它有一个隐含的内存障碍,防止看到旧副本。您将更常见地看到
volatile ProgressStatus\u状态但是
volatile
也有它的问题


您是对的,实际的读写操作在这里并不真正需要锁(访问引用是原子的)

谢谢。然而,在我看来,您提到的两个问题都可以通过使用
volatile
字段或内存屏障更明确地解决。如果我错了,请纠正我。是的,它们可以使用volatile解决,但这更难理解。如果性能需求需要,应该使用复杂的原语。在这种特定情况下使用内存屏障的一个问题是,这意味着使用本文第2部分中的某些内容,直到第4部分才进行解释。谢谢-我怀疑易失性字段可能会起作用。“
volatile
也有它的问题”-你能详细说明一下吗?