Почему вызов лямбда-выражения Python из C # не является поточно-ориентированным?

Я определяю (чистое) лямбда-выражение без побочных эффектов в 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(".");   
}

Сообщение об ошибке:

Обнаружена FatalExecutionEngineError

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

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

var calculate = new ThreadLocal<Func<double, double, double>>(() => Python.CreateEngine().Execute("lambda a,b : a+b"));
28
задан Rauhotz 28 November 2011 в 08:41
поделиться

2 ответа

Запуск аналогичного кода (см. Ниже) на 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(".");
}
0
ответ дан 28 November 2019 в 03:57
поделиться

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

4
ответ дан 28 November 2019 в 03:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: