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"));
'diversi milioni iterazioni di quale ciclo? interno, parallelo o mentre? – leppie