Статические конструкторы работают как CER? - PullRequest
3 голосов
/ 19 октября 2019

Я тестирую следующий код:

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() блокирует основной поток до тех пор, покавыполнение статического конструктора заканчивается, и в соответствии с документацией :

Поток, вызывающий Abort, может блокироваться, если прерываемый поток находится в защищенной областикод, такой как блок catch, блок finally или область выполнения с ограничениями .

Мои вопросы:

  1. Действительно ли статические конструкторы выполняются какограниченные регионы исполнения (ССВ)?
  2. Если да, то какие еще блоки кода работают как CER?

Ответы [ 2 ]

0 голосов
/ 19 октября 2019

Кажется, что статические конструкторы гарантированно завершили , почти.

Статические конструкторы можно вызывать явно с помощью RuntimeHelpers.RunClassConstructor which ' Гарантирует, что инициализатор типа (также известный как статический конструктор) для указанного типа был запущен' .

В вашем примере код можно записать следующим образом.

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

RunClassConstructor, звонки _RunClassConstructor, которые снабжены следующим комментарием.

При отсутствии условий взаимоблокировки конструктора класса вызов также гарантированно завершится.

// 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);
0 голосов
/ 19 октября 2019

Обновление: вопрос о t.Abort() блокировании, а не прерывании, поэтому этот ответ вряд ли актуален:)


Документация для Thread.Abort Method явно заявляет, что Abort может прервать статический конструктор.

Когда поток сам вызывает Abort, эффект аналогичен выдаче исключения;ThreadAbortException происходит немедленно, и результат является предсказуемым. Однако, если один поток вызывает Abort в другом потоке, прерывание прерывает любой выполняемый код. Существует также вероятность того, что статический конструктор может быть прерван. В редких случаях это может помешать созданию экземпляров этого класса в этом домене приложения.

и

Например, вызов Thread.Abort может помешать статическим конструкторамвыполнение или предотвращение освобождения неуправляемых ресурсов.


Хотя ваш пример элегантно показывает, что статический конструктор не может быть прерван, такое поведение не гарантируется.


ОБНОВЛЕНИЕ

Вот пример, где статический ctor прерывается с помощью 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
...