2016-12-11 5 views
1

私が持っていると仮定struct一定:(class部材としての)ヒープ上に割り当てられ、そしてIは、読み取り、書き込みスレッドの数を有しているC#の値型パラメータコピースレッドセーフ

struct Foo{} 

それはstructです。私は特定の関数に変数を渡す場合 :

void Bar(Foo param); 

paramがそのstructのコピーとなります。 コピー操作自体はスレッドセーフですか?

答えて

0

デフォルトでは、操作はスレッドセーフであると想定しないでください。例えば

には、以下のTypesを考慮してください。

public class A 
{ 
    public B b; 
} 

public struct B 
{ 
    public int a; 
    public int b; 
    public int c; 
    public int d; 
} 

と、次の方法:

public static void Func(B b) 
{ 
    Console.WriteLine($"{b.a}, {b.b}, {b.c}, {b.d}"); 
} 

Mainの方法があり、しばらく後にコンソールを一時停止する場合:

A a = new A(); 
new Thread(() => 
{ 
    while (true) 
    { 
     a.b.a = 5; 
     a.b.b = 5; 
     a.b.c = 5; 
     a.b.d = 5; 
    } 
}).Start(); 
while (true) 
{ 
    a.b.a = 1; 
    a.b.b = 2; 
    a.b.c = 3; 
    a.b.d = 4; 
    Func(a.b); 
} 

structsの一部が1,2,3,4および5の間に混在していることがわかります。

32ビットを超えるタイプのプリミティブ演算は、x86マシンではアトミックではなく、64ビットと同じで、x64と同じです。マルチコアプロセッサからintを操作

、のは言わせて、2つのスレッドから++someInt、それがコンパイルされていても:これはあなたがたとえば、安全でしょうint上の任意の操作を使用することができることを意味していること

ありませんincx86命令では、次のことが起こります。

  • someIntは9
  • コア1は、そのキャッシュからsomeIntを取るです。
  • コア2はキャッシュからsomeIntを受け取ります。
  • コア1の値を10に増やし、キャッシュに保存します。
  • コア2は、10の値を増やしてキャッシュに保存します。

someIntを2倍にして、まだそれはバスをロックし、コアは、関連するキャッシュの排他的所有権を持っていることを確認しますどのlock incにコンパイルされます代わりに、例えばInterlockedを使用して10

に等しいです。

+1

Downvoter私の答えを改善することができますので説明してください。 –

+0

私は同意します。私は議論のないdownvoteも持っています。私たちは間違っているかもしれませんが、それを明確にすべきです。 –

0

int以上のコピー操作はスレッドセーフではありません(これは一度にフェッチされるため、その値を妨げるものはありません)。

4バイトを超える値を取ると、値が4バイトずつ取り込まれ、それらの操作の間に別のスレッドが値の一部を変更する可能性があります。

たとえば、ローディングlongは、32ビットマシンでは別のスレッドが上書きしてから半分の古い値ともう半分の新しい値を読み取るため、スレッドセーフではありません。

これらはすべて4バイト以上を取る場合も構造体の略です。

+0

コードが64ビットプロセスでのみ実行されることがわかっている場合、64ビットコピーはスレッドセーフであると考えることは有効です。ライブラリが64ビットのみであるか、または64ビットプロセスでコードが実行されていることが検出された高速パスのみを64ビットにしています。 –

+0

逆に、32ビットのコピーであっても、32ビットアライメントでないとスレッドセーフではありませんが、パラメータコピーには適用されません。 –

+0

これは、コードが64ビットとしてコンパイルされることを前提としています。それはコンパイラスイッチによって制御されるので、私はそれに頼ることをお勧めしません。 –

関連する問題