2011-12-18 7 views
5

に読んで、私は実際にABそれらを呼び出す、私は2つのスレッドを持っている配列、1つのスレッドで更新し、別の

を求めているかのより明確な表現を与えるために質問を簡素化。それらはNameというフィールドを持つタイプFooのオブジェクトを共有し、タイプFoo[]のインデックス0の配列に格納されます。スレッドは常にシステムによって保証されている順序でインデックス0にアクセスします。したがって、スレッドBの競合状態がなくなり、スレッドAの前に取得されます。

注文はこちらです。

// Thread A 
array[0].Name = "Jason"; 

// Thread B 
string theName = array[0].Name 

私はこの順序がすでに保証され言ったように、値を読み取るスレッドBのためのない方法は、スレッドの前にありません

私は何を確実にしたいことは二つのものです:

  1. 両方のスレッドがインデックス0で最新のオブジェクトを取得します。
  2. スレッドBは常に.Nameフィールドの最新の値を取得します

volatileとマークするのはオプションではありません。実際のオブジェクトは非常に複雑で、揮発性属性が付加されていないカスタム構造体もあります。

今、1を満たすことは容易である(常に最新のオブジェクトを取得)、あなたは.VolatileReadを行うことができます。

// Thread A 
Foo obj = (Foo)Thread.VolatileRead(ref array[0]); 
obj.Name = "Jason"; 

// Thread B 
Foo obj = (Foo)Thread.VolatileRead(ref array[0]); 
string theName = obj.Name 

それとも、メモリバリアを挿入することができます。

// Thread A 
array[0].Name = "Jason"; 
Thread.MemoryBarrier(); 

// Thread B 
Thread.MemoryBarrier(); 
string theName = array[0].Name 

だから私の質問これは、条件2も満たしていますか?私が読んだオブジェクトのフィールドから常に最新の値を取得することができますか?インデックス0のオブジェクトは変更されていませんが、Nameのオブジェクトが変更されている場合インデックス0VolatileReadまたはMemoryBarrierを実行すると、インデックス0のオブジェクトのすべてのフィールドも最新の値を取得しますか?

+1

なぜあなたは配列を使用していますか? ['System.Collections.Concurrent'](http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx)名前空間を参照してください。 – Oded

+0

.NET4を使用していません。 Concurrentは、私が知る限り、コレクション自体の内容を保証し、コレクション内のオブジェクトの可能なフィールド/プロパティは保証しません。 – thr

+0

私は揮発性があなたの考え方を保証するとは思わない。 –

答えて

2

これらの解決策のいずれも、lockまたはvolatileは問題を解決しません。理由:

  1. volatileは、一つのスレッドによって変更された変数は、直ちにその変数に対する操作を並べ替えていないことも、同じデータ(すなわち、それらがキャッシュされていない)とで動作する他のスレッドに可視であることを保証します。あなたが必要とするものではありません。
  2. lockは、書き込みと読み取りが同時に行われないように保証しますが、はその順序を保証しません。どのスレッドが最初にロックを取得したかによって異なりますが、これは非決定的です。
  3. したがって

、あなたのフローがある場合:

Thread A read Name 
Thread A modify Name 
Thread B read Name 

まさにそのためには、あなたがイベント(例えばすなわちAutoresetEvent)でそれを強制する必要があります。

//Thread A 
foo[0].Name = "John"; // write value 
event.Set(); // signal B that write is completed 

//Thread B 
event.WaitOne(); // wait for signal 
string name = foo[0].Name; // read value 

この保証そのスレッドBは、Aがそれを変更するまでName変数を読み込みません。

を編集してください:上記のフローが尊重されていることを確認してください。あなたがフィールドvolatileを宣言することはできませんと言っているので、私が発注強制フェンス紹介するThread.MemoryBarrier()の使用をお勧めします:http://www.albahari.com/threading/part4.aspx

+0

これは問題ではなく、2つのスレッドが同時にアクセスすることはできません。これまで私は人々が私の質問を理解していないと思うし、私はそれを書き直す必要があります。 – thr

+0

アクセスは常に順番に行われますが、2つ(またはそれ以上)の異なるスレッドからアクセスされます。 – thr

+0

@thr:次のステートメントから: "今、Thread1がFooのNameフィールドに新しい値を格納することを保証できるようにするために"私は、Thread0がそれを更新した後にのみNameを読むためにThread1を必要とすることを理解します。 – Tudor

0

ロックが問題に対処します:詳細情報については

//Thread A 
foo[0].Name = "John"; // write value 
Thread.MemoryBarrier(); 

//Thread B 
Thread.MemoryBarrier(); 
string name = foo[0].Name; // read value 

を、この文書をチェックします私が正しく理解すればあなたは持っています。これは、ロックが暗黙の(完全な)メモリ障壁を生成するためです。

Thread.MemoryBarrierを使用して明示的にメモリバリアを使用することもできます。続きを読むhere。メモリバリア効果はx86では気付きにくいものですが、PPCなどのより緩やかなオーダーシステムではしばしばかなりのものがあります。

関連する問題