为什么在构造函数中设置字段是(或不是)线程安全的?

2024-05-24

假设您有一个像这样的简单类:

class MyClass
{
    private readonly int a;
    private int b;

    public MyClass(int a, int b) { this.a = a; this.b = b; }

    public int A { get { return a; } }
    public int B { get { return b; } }
}

我可以以多线程方式使用此类:

MyClass value = null;
Task.Run(() => {
    while (true) { value = new MyClass(1, 1); Thread.Sleep(10); }
});
while (true)
{
    MyClass result = value;
    if (result != null && (result.A != 1 || result.B != 1)) { 
        throw new Exception(); 
    }
    Thread.Sleep(10);
}

我的问题是:我会看到这个(或其他类似的多线程代码)抛出异常吗?我经常看到这样的事实:非易失性写入可能不会立即被其他线程看到。因此,这似乎可能会失败,因为对值字段的写入可能发生在对 a 和 b 的写入之前。这是否可能,或者内存模型中是否有某些东西使这种(相当常见)模式安全?如果是这样,那是什么?只读对于这个目的重要吗?如果 a 和 b 是无法原子编写的类型(例如自定义结构),这会重要吗?


编写的代码将从 CLR2.0 开始工作,因为 CLR2.0 内存模型保证所有商店都有发布语义.

发布语义:确保围栏前没有任何负载或储存物品 将在栅栏后移动。之后的说明之前可能仍然会发生 栅栏。(摘自CPOW https://rads.stackoverflow.com/amzn/click/com/032143482X第 512 页)。

这意味着在分配类引用之后不能移动构造函数初始化。

乔·达菲在他的关于同一主题的文章 http://joeduffyblog.com/2007/11/10/clr-20-memory-model/.

规则 2:所有存储都具有发布语义,即不能加载或存储 继一后移动。

还有万斯·莫里森的文章在这里 https://msdn.microsoft.com/en-us/magazine/cc163715.aspx证实了同样的情况(第 4 节:延迟初始化)。

与所有移除读锁的技术一样,图 7 中的代码 依赖于强写顺序。例如,这段代码将是 在 ECMA 内存模型中不正确,除非将 myValue 设为易失性 因为初始化 LazyInitClass 实例的写入可能是 延迟到写入 myValue 之后,允许客户端 GetValue 读取未初始化状态。在 .NET Framework 2.0 中 模型,代码可以在没有易失性声明的情况下工作。

从 CLR 2.0 开始,写入保证按顺序发生。 ECMA 标准中没有指定,只是 Microsoft 的 CLR 实现提供了这种保证。如果您在 CLR 1.0 或任何其他 CLR 实现中运行此代码,你的代码可能会被破坏.

这一变化背后的故事是:(来自CPOW https://rads.stackoverflow.com/amzn/click/com/032143482X第516页)

当CLR 2.0移植到IA64时,其最初的开发已经 发生在 X86 处理器上,因此它的处理能力较差 任意存储重新排序(IA64 允许的)。也是如此 大多数由非 Microsoft 开发人员编写的针对 .NET 的代码 针对 Windows

结果是框架中的很多代码在运行时崩溃了 IA64,特别是与臭名昭著的双重检查有关的代码 锁定模式突然无法正常工作。我们会检查这个 在本章后面的模式的背景下。但总而言之, 如果商店可以传递其他商店,请考虑这一点:一个线程可能 初始化私有对象的字段,然后发布对的引用 它位于共享位置;因为商店可以四处移动,另一个 线程可能能够看到对该对象的引用,读取它,并且 但在字段仍处于未初始化状态时查看它们。 这不仅影响现有代码,还可能违反类型系统 属性,例如 initonly 字段。

因此 CLR 架构师决定通过发布来增强 2.0 IA64 上的所有商店都作为释放栅栏。这给了所有 CLR 程序 更强的记忆模型行为。这确保了程序员不需要 必须担心微妙的竞争条件,这些条件只会在 在一个不起眼、很少使用且昂贵的架构上进行实践。

注意乔·达菲说他们通过将 IA64 上的所有商店作为释放栅栏来强化 2.0 这并不意味着其他处理器可以对其重新排序。其他处理器本身本质上提供了store-store(store后面跟着store)不会被重新排序的保证。所以 CLR 不需要明确保证这一点。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么在构造函数中设置字段是(或不是)线程安全的? 的相关文章

随机推荐