2017-11-17 11 views
0

ConcurrentBagリストを使用してforeachを連続してループしているスレッド#1があり、項目を変更していますが、別のスレッド#2がリストから項目を変更する必要があります。スレッド#2からすべてのオブジェクトを取り出し(一方がforeachでループしている間に)、オブジェクトを変更してスレッド#2のすべてのアイテムを追加しても安全ですか?それが解決しない場合は回避策がありますか?別のスレッドからConcurrentBagオブジェクトを変更する

ConcurrentBag<MyObject> list; 

スレッド1

while (1) // continuous loop 
{ 
    foreach(MyObject obj in list) 
    { 
    obj.RunMethod(); 
    Thread.Sleep(500); 
    } 
    Thread.Sleep(1000); 
} 

スレッド2

(いくつかの点でコールバックがスレッド#2に呼び出され、オブジェクトを変更する必要があることである)

List<MyObject> temp; 
while (list.TryTake(out obj) 
{ 
    MyObject obj2=obj.Clone(); // make a copy of the object 
    if (obj2.Id==4) obj2.variable=10; 
    temp.Add(obj2); 
} 
// Add objects back to concurrentbag 
foreach(MyObject obj in temp) list.Add(obj); 
+0

なぜすべてのアイテムを取り出して読み込む必要がありますか? – Evk

+0

そうすることで、どんな問題を解決しようとしていますか?コンテキストがなければ、2つのスレッドが同じオブジェクトを同時に変更しようとするのが安全かどうかを知ることは難しいです(ただし、ConcurrentBagを使用すると未定義の動作は発生しません)。 –

+0

ConcurrentBagはスレッドごとにコレクションを作成するので、アイテムを追加すると、現在のスレッドに属するコレクションに追加されます。オブジェクトを取り出すと、まずコレクションを空にしてから、他のスレッドのコレクションからオブジェクトを盗み始めます。それはスレッドセーフな方法で盗み出しますので、これは安全です。私はこれがあなたが解決している問題を探している解決策ではないと考えています。それを行うより良い方法があります。私達に情報を与え、あなたが何をすべきかアドバイスすることができます。 – john

答えて

4

いいえforeachのスレッドがオブジェクトへの参照を取得して動作している可能性があるため、これは安全ではありません他のスレッドがバッグから取り除き、変更して戻します。

これにより、スレッドがforeachスレッドを使用している間にオブジェクトの状態が変化する可能性があります。これを避けるには、変更するオブジェクトのコピーを作成し、コピーを変更してコピーをバッグに戻します。

オブジェクトを不変にすると考えましたか?そうすることで、実際には単純化され、実際には2つのスレッドがそれを同時に変更することは不可能になります。

注:オブジェクトへの参照をコピーしてもオブジェクトの内容はコピーされません。したがって、list.TakeOut(out obj)を実行すると、既存のオブジェクトへの参照を受け取り、新しいコピーを作成しません。

[編集]質問を編集してobj.Clone()を使用しました。それがあなたのオブジェクトの適切な深いクローンを行うならば、コードでなければなりませんが、変更された場所のどこでもオブジェクトを慎重にコピーしている場合のみです。クラスを不変にすると、この振る舞いを保証するので、それを使うことができればそれが最良の解決策です。

関連する問題