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



这是我感到困惑的标准部分: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. 主线程开始等待其他线程完成。
  6. otherThread 尝试在 Foo 上获取初始化锁,但由于主线程持有该锁而失败。
  7. otherThread 放弃执行静态构造函数,因为主线程持有初始化锁并等待 otherThread。
  8. otherThread 运行 SomeFunction 并成功完成。
  9. 主线程返回。

这是怎么回事?

">

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

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

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();
}

此示例不会死锁,因为一个线程正在等待另一个线程释放静态初始值设定项锁,因此当该线程最终请求原始线程具有的静态初始值设定项锁时,带引号的子句将启动,它只是跳过锁。

最新更新