Defino una expresión lambda libre de efectos secundarios (pura) en IronPython y la asigno a un delegado de C#. Al invocar al delegado simultáneamente desde múltiples hilos obtengo excepciones de tipo AccessViolationException, NullReferenceException y FatalEngineExecutionError.¿Por qué llamar a una expresión lambda de Python desde C# no es seguro para subprocesos?
La ocurrencia del error no es determinista y en su mayoría requiere varios millones de iteraciones para provocarlo, lo que me dice "condición de carrera". ¿Cómo puedo evitarlo?
Las excepciones solo se producen cuando se ejecuta el proceso con x64 (x86 no se bloquea) y fuera del depurador. El sistema de prueba es un Core I7 (8 hilos) en Windows 7, .NET Framework 4.0 e IronPython 2.7.1.
Aquí está el código mínimo para producir el error:
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(".");
}
mensaje de error:
FatalExecutionEngineError was detected
Message: The runtime has encountered a fatal error. The address of the error was at 0xf807829e, on thread 0x3da0. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.
Actualización: Incluso si el motor se declara como local de subprocesos, se bloquea después de algún tiempo:
var calculate = new ThreadLocal<Func<double, double, double>>(() => Python.CreateEngine().Execute("lambda a,b : a+b"));
'varios millones iteraciones' de qué ciclo? interno, paralelo o mientras? – leppie