2011-11-26 4 views
28

Definisco un'espressione lambda (pura) senza effetti collaterali in IronPython e la assegna a un delegato C#. Quando si richiama il delegato contemporaneamente da più thread ottengo eccezioni di tipo AccessViolationException, NullReferenceException e FatalEngineExecutionError.Perché chiamare un'espressione lambda Python da C# non è thread-safe?

L'occorrenza dell'errore non è deterministica e in genere richiede diversi milioni di iterazioni per provocarlo, il che significa "condizione di gara" per me. Come posso evitarlo?

Le eccezioni vengono sollevate solo quando si esegue il processo con x64 (x86 non si blocca) e all'esterno del debugger. Il sistema di test è un Core I7 (8 thread) su Windows 7, .NET Framework 4.0 e IronPython 2.7.1.

Ecco il codice minimo per produrre l'errore:

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("."); 
} 

Messaggio di errore:

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.

Aggiornamento: Anche se il motore viene dichiarata come thread-local, si blocca dopo qualche tempo:

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

'diversi milioni iterazioni di quale ciclo? interno, parallelo o mentre? – leppie

risposta

4

Ciò è probabilmente dovuto a una condizione di competizione all'interno di ScriptEngine. Si noti che ScriptEngine.Execute restituisce un riferimento a una funzione Python anziché a Func (è dovuto al comportamento dinamico di C#, che è possibile trattare il risultato come Func. Non sono esperto su IronPython ma guardando all'origine di PythonFunction, c'è alcuna indicazione che si tratta di threadsafe.

0

esecuzione di codice simile (vedi sotto) sulla IronScheme, non pone tali questioni.

L'unica cosa che mi viene in mente, è che engine.Execute("lambda a,b : a+b") crea una funzione interpretato invece di un compilato 1. Combina quello con un interprete probabilmente non sicuro di thread e 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("."); 
}