2009-05-14 7 views
4

以下のコードを見てください。これがENTIREクラスであると仮定してください。私はすべてのコードを省略していません。これは文字通りすべてのことです。C#マルチスレッド

私のメインプログラムのループでこのクラスをインスタンス化し、myExample.Add(何でも)を時々呼び出すと、Dequeue()およびEnqueue()をロックしないことによって発生する問題を心配する必要がありますか?

public class Example<T> 
{ 
    private Queue<T> q = new Queue<T>(); 

    public Example() 
    { 
     new Thread(() => 
     { 
      while (true) 
      { 
       if (this.q.Count > 0) 
       { 
        var item = this.q.Dequeue(); 
       } 
       Thread.Sleep(1); 
      } 
     }).Start(); 
    } 

    public void Add(T val) 
    { 
     this.q.Enqueue(val); 
    } 
} 

this.q.Enqueue(val)がthis.q.Dequeue()と同時に呼び出されるとどうなりますか?

+0

(あなたのコメントに返信しました) –

答えて

2

です。 :)複数のスレッドを持つExampleを呼び出すと、キューのカウントとデキューのチェックの競合状態になります。あなたが複数のスレッドを例に呼び出さない場合は、あなたが離陸何を気にしないので、例えば...

T1 Call Example 
T2 Call Example 
T2 check Q.Count > 0 (yes) 
T1 check Q.count > 0 (yes) 
T2 dequeue 
T1 dequeue OOPS! 

は、その後、あなたが何かを取ることだけが、何の問題もありません。離陸したものを気にかけていたら、AddとExampleの競合状態が再び問題になります。

+0

私はこれを最初に考えていましたが、キューが1つのループの中にある唯一の場所です... Exampleクラスの外部のものはキューに入れることができ、キューを見ることはできません。 – uzbones

+0

Exampleに呼び出される各スレッドは、そのループに入る新しいスレッドをフォークします。例に10個のスレッドを送ると、そのループで10個のスレッドが実行されます。それらのスレッドは決して戻ってこない。だから、それは時間が経つと増え、あなたは競争状態になります。 –

+0

これらは静的なクラスではないので、それらはすべて別々のオブジェクトになり、コンストラクタはスレッドを作成するものなので、オブジェクトごとに常に1つのスレッドになります。 – uzbones

0

私はあなたが

this.q.Enqueue(val); 

を意味

this.queue.Enqueue(val); 

によって仮定?

もしそうなら、ある種のロックを実行する必要があります。コレクションは本質的にスレッドセーフではありません。あなたが常にあるスレッドから削除して、常に別のスレッドから追加していることを考えれば、うまくいくでしょうが、それを安全にプレイしないのはなぜですか?

+0

キューはいつでも内部配列のサイズを変更する可能性があるので、安全とは思わないでしょう。 –

+0

完全にキューに入る前に何かをデキューしようとしたり、内部配列の同じ位置に2つの項目を書き込んだりする可能性があるので、エンキューに問題があるかもしれないと私は同意します。 – uzbones

1

クリス

readonly static object lockObject = new object(); 

プライベート静的フィールドを追加するには、ラムダのため

lock(lockObject) 
{ 
    //do stuff 
} 

とデキューとエンキューを囲み、あなたがしているので、ループ

1

外に置きますデキューメソッドを使用してキューに書き込むには、キューをロックする必要があります。キューは複数のリーダーに対してスレッドセーフですが、複数のライターではありませんQueue thread safety

+0

このシナリオの「リーダー」は、デキューではなくforeachであることに注意してください(これは「書き込み」です)。など;実際には、キューが複数の発信者を有効にサポートしていないと言うのは簡単です。まったく。 –

0

なぜ安全だと思うかわかりません。

私の心配は、(2つの異なるスレッドから)エンキューとデキューを同時に入力すると、キューインスタンスの内部状態が壊れる可能性があるということです。

8

ショートバージョンあなたはそれが機能するために予想される場合キューへのアクセスを同期する必要がありますです。カウント/デキューには明白なスレッドレースがありますが、さらに重要なことはの動作が保証されています - 内部状態がのいずれかであるを正当に行う可能性があり、破損する可能性があります。

最終的にスレッドを終了する方法も必要です。

ブロックキューを作成するには、Creating a blocking Queue<T> in .NETを参照してください。

+0

しかし、なぜですか?マーク、私はあなたが私よりもスマートだと知っていますが、もしあなたがこれらの点を得るなら、明らかな競争条件が何であるか教えてください。私は内部状態についてあなたに同意しますが、競争条件はどうですか? – core

+0

OK - この場合競合状態*はおそらく重要ではありません。複数の読者が存在することはありません。しかし、スレッディングは難しいです:競合状態(ロックなし)がないことを確認するには、コードを読んで呼び出しパターンを理解する必要があります。カウント/デキューをロックすることでレースの可能性がないことを明白にしてみませんか?これはまた、後でクラスを拡張するときにバグの狩りに行く必要がないことを意味します。 –

0

キューを変更するスレッドを明示的に作成しているため、呼び出すスレッドに関係なく、Add()を呼び出すことは安全です。

"if"ステートメントブロックをロック()する必要があります。なぜなら、q.Countを呼び出しても安全ではないため、Add()メソッドで再びロックするからです。

関連する問題