Почему вызов лямбда-выражения Python из C # не является потокобезопасным? - PullRequest
28 голосов
/ 26 ноября 2011

Я определяю лямбда-выражение без побочных эффектов (чистый) в IronPython и назначаю его делегату C #.При одновременном вызове делегата из нескольких потоков я получаю исключения типа AccessViolationException , NullReferenceException и FatalEngineExecutionError .

Возникновение ошибки не-детерминированный, и для его провоцирования требуется несколько миллионов итераций, что говорит мне о «состоянии гонки».Как я могу избежать этого?

Исключения возникают только при запуске процесса с x64 (x86 не падает) и вне отладчика.Тестовая система - Core I7 (8 потоков) в Windows 7, .NET Framework 4.0 и IronPython 2.7.1.

Ниже приведен минимальный код для выдачи ошибки:

var engine = Python.CreateEngine();

double a = 1.0;
double b = 2.0;

while (true)
{
    Func<double, double, double> calculate = engine.Execute("lambda a,b : a+b");

    System.Threading.Tasks.Parallel.For(0, 1000, _ =>
    {
         for (int i = 0; i < 1000; i++) { calculate(a,b); }
    });

    Console.Write(".");   
}

Ошибкасообщение:

Обнаружена ошибка FatalExecutionEngineEr *

Сообщение: во время выполнения обнаружена фатальная ошибка.Адрес ошибки был 0xf807829e, в потоке 0x3da0.Код ошибки 0xc0000005.Эта ошибка может быть ошибкой в ​​CLR или в небезопасных или не поддающихся проверке частях пользовательского кода.Распространенные источники этой ошибки включают в себя ошибки пользовательского маршалинга для COM-взаимодействия или PInvoke, которые могут повредить стек.

Обновление: даже если ядро ​​объявлено как локальное для потока, через некоторое время происходит сбой:

var calculate = new ThreadLocal<Func<double, double, double>>(() => Python.CreateEngine().Execute("lambda a,b : a+b"));

Ответы [ 3 ]

4 голосов
/ 28 ноября 2011

Вероятно, это связано с состоянием гонки в ScriptEngine.Обратите внимание, что ScriptEngine.Execute возвращает ссылку на PythonFunction, а не на Func (это связано с динамическим поведением C #, что вы можете рассматривать результат как Func. Я не эксперт по IronPython, но, глядя на источник PythonFunction, естьнет никаких указаний на то, что это потокобезопасно.

1 голос
/ 28 ноября 2011

Вы пытались заменить 64-битный тип данных (double) на 32-битный тип данных (float)?Я знаю, что «Простое чтение или запись в поле размером 32 бита или менее всегда является атомарным» , тогда как в 64-битном поле могут возникнуть проблемы в зависимости от процессора и кода.Это похоже на длинный выстрел, но стоит попробовать.

0 голосов
/ 06 декабря 2011

Запуск аналогичного кода (см. Ниже) на IronScheme, таких проблем не возникает.

Единственное, о чем я могу думать, это то, что engine.Execute("lambda a,b : a+b") создает интерпретированную функцию вместо скомпилированной.Добавьте к этому, возможно, небезопасный интерпретатор потока и kaboom.

double a = 1.0;
double b = 2.0;

while (true)
{
  var calculate = "(lambda (a b) (+ a b))".Eval<Callable>();

  System.Threading.Tasks.Parallel.For(0, 1000, _ =>
  {
    for (int i = 0; i < 1000; i++) { calculate.Call(a, b); }
  });

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