C#中的静态构造函数死锁是否与ECMA CLI标准相矛盾?

时间:2018-09-07 21:03:53

标签: c# multithreading deadlock static-constructor

这是我对标准感到困惑的部分:http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf#page=178&zoom=auto,87,610%22

  

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

     

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

     

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

以下代码在我测试时会死锁,这似乎与标准相矛盾:

public static class Foo {
    static Foo() {
        var otherThread = new Thread(() => { Thread.Sleep(1000); SomeFunction(); });
        otherThread.Start();
        otherThread.Join();
    }
    public static void SomeFunction() {
    }
}
class Program {
    static void Main() {
        Foo.SomeFunction();
    }
}

根据标准,我希望发生以下情况:

  1. 主线程对Foo进行初始化锁定。
  2. 主线程运行Foo的静态构造函数。
  3. 主线程创建otherThread并启动它。
  4. otherThread开始等待一秒钟,从而确保第5点发生在第6点之前。
  5. 主线程开始等待otherThread完成。
  6. otherThread尝试获取Foo上的初始化锁,但由于主线程持有该锁而失败。
  7. otherThread放弃执行静态构造函数,因为主线程持有初始化锁并等待otherThread。
  8. otherThread运行SomeFunction并成功完成。
  9. 主线程返回。

这是怎么了?

1 个答案:

答案 0 :(得分:2)

“等待此线程完成的任何线程”是指等待使用静态线程的初始化锁的所有线程,而不是等待使用任何可能的同步机制的线程。静态初始化机制无法知道其他线程正在使用另一个完全不同的机制在另一个线程上等待。

引用的部分是指以下示例不会死锁的事实:

public class A
{
    static A()
    {
        Thread.Sleep(TimeSpan.FromSeconds(1));
        B.DoNothing();
    }
    public static void DoNothing() { }
}
public class B
{
    static B()
    {
        Thread.Sleep(TimeSpan.FromSeconds(1));
        A.DoNothing();
    }
    public static void DoNothing() { }
}
private static void Main()
{
    Task.Run(() => B.DoNothing());
    A.DoNothing();
}

此示例不会死锁,因为一个线程正在等待另一个线程释放静态初始值设定项锁定,因此当该线程最终要求原始线程拥有的静态初始值设定项锁定时,带引号的子句就会踢进去,跳过锁。