2011-11-26 2 views
28

IronPythonで副作用のない(純粋な)ラムダ式を定義し、それをC#デリゲートに割り当てます。デリゲートを複数のスレッドから同時に呼び出すと、タイプの例外が発生します。アクセシビリティ例外NullReferenceExceptionおよびFatalEngineExecutionErrorなぜC#のPythonラムダ式をスレッドセーフではないと呼びますか?

エラーの発生は非決定論的であり、それを誘発するために大部分は数百万回の反復が必要となります。これは私にとって「競合状態」です。どうすればそれを避けることができますか?

x64(x86はクラッシュしない)でプロセスを実行し、デバッガの外部にある場合にのみ例外が発生します。テストシステムは、Windows 7、.NET Framework 4.0およびIronPython 2.7.1のCore I7(8スレッド)です。

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 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.

アップデート:エンジンはスレッドローカルとして宣言されている場合でも、それはいくつかの時間後にクラッシュ:

はここでエラーを生成するために、最小限のコードです

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

'数百万どのループの反復?インナー、パラレル、またはその間? – leppie

答えて

1

64ビットデータ型(double)を32ビットデータ型(float)に置き換えてみましたか?私は"A simple read or write on a field of 32 bits or less is always atomic"を知っていますが、64ビットフィールドはプロセッサとコードによって問題を抱えることがあります。それは長いショットのようだが、それは試してみる価値がある。

+0

doubleをfloatに変更することは役に立ちません。 – Rauhotz

4

これは、ScriptEngine内の競合状態が原因と考えられます。 ScriptEngine.Executeは、FuncではなくPythonFunctionへの参照を返します(これは、C#の動的な振る舞いのため、結果をFuncとして扱うことができるためです)。私はIronPythonの専門家ではなく、PythonFunctionのソースを見ています。それはスレッドセーフであることを全く表示。

0

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