Оператор Plinq блокируется внутри статического конструктора - PullRequest
8 голосов
/ 24 апреля 2011

Я сталкивался с такой ситуацией, когда следующий оператор plinq внутри статического конструктора блокируется:

static void Main(string[] args)
{
    new Blah();
}

class Blah
{
     static Blah()
     {
         Enumerable.Range(1, 10000)
            .AsParallel()
            .Select(n => n * 3)
            .ToList();
     }
}

Это происходит только тогда, когда конструктор является статическим.Может кто-нибудь объяснить мне, пожалуйста.

Это ошибка TPL?Компилятор?Me

Ответы [ 3 ]

9 голосов
/ 25 апреля 2011

Обычно опасно вызывать многопоточный код из статического конструктора. Чтобы гарантировать, что статический конструктор выполняется только один раз, CLR выполняет статический конструктор под блокировкой. Если поток, выполняющий статический конструктор, ожидает во вспомогательном потоке, существует риск того, что вспомогательному потоку по какой-либо причине потребуется внутренняя блокировка CLR, и программа будет заблокирована.

Вот более простой пример кода, демонстрирующий проблему:

using System.Threading;
class Blah
{
    static void Main() { /* Won’t run because the static constructor deadlocks. */ }

    static Blah()
    {
        Thread thread = new Thread(ThreadBody);
        thread.Start();
        thread.Join();
    }

    static void ThreadBody() { }
}

Раздел 10.5.3.3 «Гонки и тупики» спецификации ECMA CLI гарантирует следующее:

Инициализация типа сама по себе не должна создавать тупик, если какой-либо код вызывается из инициализатора типа (прямо или косвенно) явно вызывает блокирующие операции.

Таким образом, инициализатор типа (то есть статический конструктор) не будет тупиковым при условии, что никакая операция в статическом конструкторе не блокирует поток. Если статический конструктор блокирует, это может привести к тупику.

3 голосов
/ 27 апреля 2011

Хотя причина уже объяснена, почему вы не хотите выполнять потоковую работу внутри статического конструктора, я подумал, что добавлю, что «правильный» способ сделать это - использовать статический Lazy<T>.Это также более эффективно, поскольку работа по генерированию этих ресурсов будет отложена до тех пор, пока эти ресурсы действительно не потребуются.

class Blah
{
    // Supply factory method to generate the numbers, but actual generation will be deferred
    private static Lazy<List<int>> MyMagicNumbers = new Lazy<List<int>>(Blah.GenerateMagicNumbers);

    public void DoSomethingWithMagicNumbers()
    {
        // Call to Lazy<T>.Value will synchronize any calling threads until value is initially generated from the factory
        List<int> magicNumbers = Blah.MyMagicNumbers.Value;

        // ... do something here ...
    }

    private List<int> GenerateMagicNumbers()
    {
        return Enumerable.Range(1, 10000)
                          .AsParallel()
                          .Select(n => n * 3)
                          .ToList();
    }
}
2 голосов
/ 24 апреля 2011

Для чего стоит проблема не возникает на Mono:

[mono] /tmp @ dmcs par.cs 
[mono] /tmp @ mono ./par.exe 

У вас есть скомпилированный бинарный файл для Windows, чтобы я мог сравнить сгенерированный MSIL? Я не уверен, что это проблема только для библиотек, и мне любопытно:)


Сравнение IL было немного запутанным, поэтому я решил просто попробовать оба бинарных файла на обеих платформах. Хе-хе, я восстановил свою старую виртуальную машину Windows только для того, чтобы проверить это:

Запуск скомпилированных двоичных файлов VS на Mono не проблема. Вы можете попробовать это в Windows, используя 2.10.1 (http://www.go -mono.com / mono-downloads / download.html), только 77.4Mb:)

( Я использовал моно 2.11, специально созданное для Linux, поэтому возможно, что поддержка функции еще не завершена )

     \ run on platform:      MS.Net 4.0      Mono 2.1x
built on: -------------+----------------------------------------
    Visual Studio       |      deadlock       no deadlock
                        |
    MonoDevelop         |      deadlock       no deadlock

Я также заметил, что при работе в Windows CTRL-C может вырваться из замка. Будет опубликовано, если я найду еще что-нибудь.


Обновление 2

Что ж, установка Mono обходит установку VSExpress даже на Windows. Установка моно закончилась за 4 минуты, и в результате:

C:\Users\Seth>"c:\Program Files (x86)\Mono-2.10.1\bin\mono.exe" ConsoleApplication2.exe
C:\Users\Seth>

Нет тупика :) Теперь все, что остается, - это ждать, пока VSExpress будет установлен (навсегда), и все равно будут отлаживать инструменты (неизвестно), а затем иметь трещину (вероятно, до поздней ночи). CU позже

...