【问题标题】:Atomicity of object references对象引用的原子性
【发布时间】:2023-03-30 09:55:01
【问题描述】:

我刚刚遇到一个问题 - 在 C# 中跨线程安全地访问对象是否可行。

例如用代码

//Somewhere long ago
MyClass myVar = new MyClass("First Instance");

//Then later, happening at the same time
//In thread one
myVar = new MyClass("Second Instance");

//In thread two
myVar.PrintName();

我不在乎是使用第一个实例还是第二个实例,但是 myVar 是否可能在某些时候根本无效(例如,指向一个不存在的位置,因为对象引用可能只是部分更新在其他线程中使用之前)


关于锁的附加问题:

如果我留下一个锁 - 那么所有未完成的写入都会提交到内存吗?

我的问题是如果我在锁中引用一个变量,我知道只有一个线程可以同时访问锁 - 但是我是否会在一个线程中写入变量(并且写入只是完成到缓存),然后在另一个线程中甚至在锁内部获取变量的旧值(因为缓存没有提交或者我在这个线程的缓存中仍然有一个旧值)?

【问题讨论】:

    标签: .net multithreading locking


    【解决方案1】:

    给你的第一个。它几乎可以肯定是安全的,但我还是会锁定它。

    关于你的缓存问题,你在说什么缓存,我唯一能想到的就是 CPU 缓存,如果我们担心这些缓存是否很脏并且需要刷新,我们都会遇到很多麻烦麻烦。

    编辑: 从您的评论中,我看到 CPU 缓存就是您所指的。

    多核和多处理器硬件确保不会使用缓存中的任何过期数据。

    【讨论】:

    • 是的,我的意思是 CPU 缓存 - 所以锁定区域内的代码确保“缓存干净”?
    • 所有代码都是“缓存清理”的,我们不必担心,这是硬件问题;)。在任何情况下,线程安全编码可能仍需要进一步工作以使“缓存干净”。即使使用单处理器/缓存,OTH 线程不安全代码也是不安全的。
    【解决方案2】:

    我知道您的问题是关于 .NET 的,但我无法在那里告诉您答案。但是,在 Java 中,这段代码肯定是不安全的。

    风险在于代码重新排序(优化步骤)会在构造函数完成运行之前看到对象引用被更新 - 因此第二个线程可能会看到第二个对象的部分构造版本。

    在 java 中,您可以通过使对象引用变为 volatile 或将其保护在某种 AtomicReference 后面(当然,或者通过同步)来解决这个问题——其中任何一个都会施加“内存屏障”,从而限制可以进行的优化这样做是为了确保部分构造的对象永远不可见。

    正如我所说,我对 .NET 内存模型了解得不够多,无法知道 .NET 是否会出现同样的问题。

    【讨论】:

      【解决方案3】:

      您问题中的代码示例是线程安全的。显然取决于您在该构造函数中执行的操作,但“通常” myVar 引用将是有效的(通常因为您可以执行一些低级别的操作,例如 FormatterServices 等)。将使用构造函数创建一个新对象,并自动返回到正确的线程。对我来说,通过在每个线程上创建新对象来确保代码是线程安全的,这是最好的方法之一。

      【讨论】:

        【解决方案4】:

        我想说,只要您没有明确取消引用对象,就不会有空引用的威胁。唯一的可能是在初始化 myVar 之前启动线程二。在这个简单的示例中,我认为您不需要锁。至于您的附带问题,您指的是哪个缓存?

        【讨论】:

          猜你喜欢
          • 2012-07-12
          • 1970-01-01
          • 1970-01-01
          • 2010-12-16
          • 1970-01-01
          • 2012-08-30
          • 2023-03-21
          • 1970-01-01
          • 2016-10-09
          相关资源
          最近更新 更多