【问题标题】:How to safely work around BeforeFieldInit and static constructor cycles?如何安全地解决 BeforeFieldInit 和静态构造函数循环?
【发布时间】:2019-08-07 00:04:44
【问题描述】:

我担心以下两种行为之间的相互作用:

http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf#page=179

2.1。如果类型尚未初始化,请尝试获取初始化锁。

2.2.1。如果不成功,查看该线程或等待该线程完成的任何线程是否已经持有锁。

2.2.2。如果是这样,则返回,因为阻塞会造成死锁。该线程现在将看到该类型的未完全初始化状态,但不会出现死锁。

http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf#page=69

如果标记为 BeforeFieldInit,则该类型的初始化方法在首次访问为该类型定义的任何静态字段时或之前的某个时间执行。

此代码示例演示了可能的交互:

static class Foo {
  public static int A = 1;
}
static class Bar {
  public static int B = Foo.A;
}
static class Program {
  static void Main() {
    Console.WriteLine(Bar.B);
  }
}

在任何健全的环境中测试时,它会输出 1。但是,规范似乎允许它输出 0,方法是执行以下操作:

  1. Main 开始执行。
  2. Foo 的类型初始化程序开始执行(由于 BeforeFieldInit 规则,此时允许发生这种情况)。
  3. Bar 的类型初始化程序开始执行(由于 BeforeFieldInit 规则,此时允许发生这种情况)。
  4. Bar.B 的初始化程序开始执行。
  5. 请求Foo.A。
  6. Foo 的类型初始化程序已经在运行,等待它会导致死锁。死锁规则允许我们在未完全初始化的状态下查看 Foo,其中 A 尚未设置为 1,仍具有其默认值 0。
  7. Bar.B 设置为 0。
  8. Bar 的类型初始化器已完成。
  9. Foo.A 设置为 1。
  10. Foo 的类型初始化程序已完成。
  11. 主输出 Bar.B,即 0。

这真的允许吗?我应该如何编写类型初始化器,以免被它咬伤?

【问题讨论】:

    标签: c# .net deadlock static-initialization static-constructor


    【解决方案1】:

    这真的允许吗?

    看起来确实是规范允许的。

    在任何健全的环境中测试时,它会输出 1。

    没错。了解优化背后的原因很有帮助。 “宽松语义”的目的是移动对“静态构造函数是否运行?”的检查。从访问类型的执行时间访问该类型的方法的jit时间。也就是说,如果我们有:

    void M()
    {
        blah
        if blah blah
        ... Foo.A ...
        if blah blah blah
        ... Foo.A ...
        blah blah blah
    }
    

    假设M 到了jit 时间,Foo 的 cctor 还没有执行。为了严格遵守,抖动必须在每次访问 Foo.A 时生成代码,以检查 Foo cctor 是否已执行,如果尚未执行,则执行它。

    但是如果我们在jit时间调用cctor,jitter知道FooM内部被访问,所以可以在@987654327时调用cctor @被jitted,然后跳过在M内生成每个检查。

    jitter 足够聪明,可以在 jit 时间执行 cctor 时做正确的事情;它不会按照您描述的“错误”顺序执行 cctor,因为编写 jitter 的人是理智的人,他们只是想让您的代码更快。

    我应该如何编写类型初始化器,以免被它所困扰?

    您应该假设符合标准的实现的作者是理智的。

    如果出于某种原因您不能假设:您可以将所有您关心的静态字段初始化器放入静态构造函数中。 C# 编译器不允许在具有静态构造函数的类型上使用 BeforeFieldInit 语义。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-05
      • 2011-03-01
      • 1970-01-01
      • 2010-09-12
      • 1970-01-01
      • 2014-03-14
      • 2011-02-20
      相关资源
      最近更新 更多