2016-06-20 12 views
2

このコードがこのコードのスレッドセーフであるかどうかを知りたいですか?参照型はスレッドセーフですが、このケースでは一般的なので、私たちは正しいことができないので、私はそう思うでしょうか?コードはスレッドセーフですか?

public class LifoMsgQueue<T> 
    { 
     private class Node<E> 
     { 
      internal Node<E> next; 
      internal E msg; 
      internal Node(E msg) 
      { 
       this.msg = msg; 
      } 
     } 
     private Node<T> top; 
     public void Send(T msg) 
     { 
      Node<T> node = new Node<T>(msg); 
      node.next = top; 
      top = node; 
     } 
     public T Receive() 
     { 
      SpinWait sw = new SpinWait(); 
      Node<T> oldTop; 
      while ((oldTop = top) == null) 
       sw.SpinOnce(); 
      top = oldTop.next; 
      return oldTop.msg; 
     } 
    } 
+7

"参照型はスレッドセーフです"! – user2864740

+0

2つのスレッドが同時に 'Send'を呼び出すと、それはすべて終わりです。 –

+1

同期が表示されていない場合、このコードはスレッドセーフではありません。ローカルの 'SpinWait sw'変数を使用すると、同期としてカウントされません。 –

答えて

4

あなたのコードのスレッドセーフを作成する最も簡単な方法は、私が思うに、カスタムのいずれかの使用を置き換えることであろう「LifoMsgQueue <T>」System.Collections.Concurrent.ConcurrentStack <T>クラスで、それは同じことをするだろう。

https://msdn.microsoft.com/en-us/library/dd267331(v=vs.110).aspx

を見る(LIFO)コレクション内の-最初のスレッドセーフな最後を表します。

クラスをスレッドセーフにしたい場合は、lockキーワードなどのロック機構を調べる必要があります。

3

複数の「プロデューサ」(Send()の呼び出し元)が複数ある場合、これは決して安全ではありません。 2つのスレッドは、同じ.top、または競合して上書きすることができます。

複数の「コンシューマ」(一般的ではないが、あなたが求める安全性の程度を正確に指定しなかった場合、たとえば1消費者x 1プロデューサ、1コンシューマx Nプロデューサ、Nコンシューマx 1プロデューサ、N個の消費者×N個の生産者)は、メッセージを複製するリスクがあります。

ここでのジェネリックの使用は意味を持ちません。参照型では、オブジェクト参照が単なる単語であり、Nodeがクラスであるため、読み込みを乱すことはできませんが、競合状態になりやすいです。

静的クラスInterlockedには、IProducerConsumer機能に頼るのではなく、自分で実装することに興味がある場合に、これらの問題を緩和できるツールが多数用意されています。

関連する問題