Взаимные блокировки статического конструктора в C # противоречат стандарту ECMA CLI? - PullRequest
0 голосов
/ 08 сентября 2018

Вот раздел стандарта, который меня смущает: http://www.ecma -international.org / публикации / файлы / 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. Возвращается основной поток.

Что здесь не так?

1 Ответ

0 голосов
/ 08 сентября 2018

«любой поток, ожидающий завершения этого потока» относится к любому потоку, ожидающему , использующему блокировки инициализации для статических потоков , а не к потоку, ожидающему с использованием какого-либо возможного механизма синхронизации.Механизм статической инициализации не может знать, что какой-то другой поток ожидает , используя какой-то совершенно другой механизм в другом потоке.

В цитируемом разделе говорится о том, что приведенный ниже пример выиграл'deadlock:

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...