静态构造函数 运行 是否作为 CER?

Do static constructors run as CERs?

我正在测试以下代码:

private static void Main()
{
    var t = new Thread(() => 
    {
        var m = new MyCls();
        m.DoWork();       
    });
    t.Start();

    // simulate time-consuming job
    Thread.Sleep(1000);

    t.Abort();
    Console.Write("\nDone");
}


public class MyCls
{
    static MyCls()
    {
        for (int i = 0; i < 10; i++)
        {
            Console.Write(i);
            Thread.Sleep(1000);
        } 
    }

    public void DoWork()
    {
        Console.WriteLine("executing DoWork..");
    }
}

我得到的输出是:

0123456789
Done

似乎 t.Abort() 调用阻塞了主线程,直到静态构造函数的执行完成,并且根据 documentation:

The thread that calls Abort might block if the thread that is being aborted is in a protected region of code, such as a catch block, finally block, or constrained execution region.

我的问题是:

  1. 静态构造函数真的 运行 作为受限执行区域 (CER) 吗?
  2. 如果是,还有哪些其他代码阻止 运行 作为 CER?

更新:问题是关于 t.Abort() 阻塞,而不是中断,所以这个答案几乎不相关:)


Thread.Abort Method 的文档明确指出 Abort 可能会中止静态构造函数。

When a thread calls Abort on itself, the effect is similar to throwing an exception; the ThreadAbortException happens immediately, and the result is predictable. However, if one thread calls Abort on another thread, the abort interrupts whatever code is running. There is also a chance that a static constructor could be aborted. In rare cases, this might prevent instances of that class from being created in that application domain.

For example, calling Thread.Abort may prevent static constructors from executing or prevent the release of unmanaged resources.


虽然您的示例优雅地表明静态构造函数可能不会被中断,但不能保证这种行为。


更新

这是一个静态构造函数被 Abort() 中断的示例。

代码

public static void Main()
{
    Console.WriteLine($"START");
    var t = new Thread(() =>
    {
        var m = new MyCls();
    });
    Console.WriteLine($"t.Start");
    t.Start();

    Thread.Sleep(2000);
    Console.WriteLine($"Trying to create a new object");

    try
    {
        var m2 = new MyCls();

    }
    catch (Exception ex) { Console.WriteLine(ex); }
    Console.WriteLine("All done");
    Console.ReadLine();
}


public class MyCls
{
    static MyCls()
    {
        for (int i = 0; i < 10; i++)
        {
            if (i == 4)
            {
                Console.WriteLine($"sctor calling Abort on itself");
                Thread.CurrentThread.Abort();
            };
            Console.WriteLine($"sctor.{i}");
            Thread.Sleep(100);
        }
    }
}

输出

START
t.Start
sctor.0
sctor.1
sctor.2
sctor.3
sctor calling Abort on itself
Trying to create a new object
System.TypeInitializationException: The type initializer for 'MyCls' threw an exception. ---> System.Threading.ThreadAbortException: Thread was being aborted.
   at System.Threading.Thread.AbortInternal()
   at System.Threading.Thread.Abort()
   at Program.MyCls..cctor() in c:\users\letss\source\repos\ConsoleApp2\ConsoleApp2\Program.cs:line 42
   --- End of inner exception stack trace ---
   at Program.MyCls..ctor()
   at Program.Main() in c:\users\letss\source\repos\ConsoleApp2\ConsoleApp2\Program.cs:line 21
All done

看来静态构造函数保证已经完成,差不多了。

可以使用 RuntimeHelpers.RunClassConstructor 显式调用静态构造函数,其中'确保指定类型的类型初始值设定项(也称为静态构造函数)已 运行'.

在你的例子中,代码可以这样写。

var t = new Thread(() =>
{
  System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(MyCls).TypeHandle);
  var m = new MyCls();
});

RunClassConstructor 调用 _RunClassConstructor 并用以下注释进行注释。

In the absence of class constructor deadlock conditions, the call is further guaranteed to have completed.

// RunClassConstructor causes the class constructor for the given type to be triggered
// in the current domain.  After this call returns, the class constructor is guaranteed to
// have at least been started by some thread.  In the absence of class constructor
// deadlock conditions, the call is further guaranteed to have completed.
//
// This call will generate an exception if the specified class constructor threw an 
// exception when it ran. 
[System.Security.SecuritySafeCritical]
[ResourceExposure(ResourceScope.None)]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private static extern void _RunClassConstructor(RuntimeType type);