2011-11-26 9 views
28

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")); 
+1

'varios millones iteraciones' de qué ciclo? interno, paralelo o mientras? – leppie

Respuesta

1

¿Ha intentado reemplazar el tipo de datos de 64 bits (doble) con un tipo de datos de 32 bits (flotante)? Sé que "A simple read or write on a field of 32 bits or less is always atomic" mientras que un campo de 64 bits puede tener problemas dependiendo del procesador y el código. Parece una posibilidad remota, pero vale la pena intentarlo.

+0

Cambiar el doble para flotar no ayuda. – Rauhotz

4

Esto es probablemente debido a una condición de carrera dentro de ScriptEngine. Tenga en cuenta que ScriptEngine.Execute devuelve una referencia a una función de Python en lugar de a un Func (se debe al comportamiento dinámico de C#, que puede tratar el resultado como un Func. No soy un experto en IronPython, pero miro la fuente de PythonFunction, hay hay indicación alguna de que es multi-hilo.

0

Ejecución de código similar (véase más adelante) en IronScheme, no plantea ningún tipo de problemas.

lo único que se me ocurre, es que engine.Execute("lambda a,b : a+b") crea una función interpretado en lugar de un compilado uno. Combine eso con un hilo posiblemente intérprete inseguro y 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("."); 
} 
Cuestiones relacionadas