【问题标题】:Do static constructor deadlocks in C# contradict the ECMA CLI standard?C# 中的静态构造函数死锁是否与 ECMA CLI 标准相矛盾?
【发布时间】:2018-09-07 21:03:53
【问题描述】:

这是标准中我感到困惑的部分: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. 主线程返回。

这里有什么问题?

【问题讨论】:

  • 请注意,规范的这一部分实际上是一个基本原理,它更像是评论而不是正式的保证或规则。它概述了提供实际保证的假设算法,但实现不需要实现这一点。实际的保证是“单独的类型初始化不应创建死锁除非从类型初始化程序(直接或间接)调用的某些代码显式调用阻塞操作。” (强调我的)。给定的示例通过在初始化程序中等待 (Join) 来明确违反此条件。
  • 在我的 2013 年关于这个主题的文章的 cmets 中有一些关于这个问题的讨论:ericlippert.com/2013/01/31/the-no-lock-deadlock

标签: c# multithreading deadlock static-constructor


【解决方案1】:

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

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

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

这个例子没有死锁,因为一个线程正在等待另一个线程释放静态初始化锁,所以当那个线程最终请求原始线程拥有的静态初始化锁时,引用的子句就会启动,它只是跳过锁。

【讨论】:

    猜你喜欢
    • 2012-08-23
    • 1970-01-01
    • 1970-01-01
    • 2016-10-06
    • 1970-01-01
    • 1970-01-01
    • 2020-10-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多