2016-06-19 7 views
1

私は別のスレッドからデータを移動したいが、私のコードは唯一の代わりにリストに最初の5つの値を保存し、その後 それらをプリントアウトの私が合格最初の値のために働くには、ここに私のコードです:スレッド間でデータを渡すにはどうすればよいですか?

private readonly ConcurrentQueue<int> _queue = new ConcurrentQueue<int>(); 
    private readonly AutoResetEvent _signal = new AutoResetEvent(false); 

    public void Thread1() 
    { 
     List<int> values = new List<int>(); 
     int lastInput; 
     StringBuilder sb = new StringBuilder(); 

     while (values.Count < 5) 
     { 
      _signal.WaitOne(); 
      _queue.TryDequeue(out lastInput); 

      values.Add(lastInput); 
     } 
     for (int i = 0; i < values.Count; i++) 
     { 
      sb.Append(String.Format("{0}\n", values[i])); 
     } 
     MessageBox.Show(sb.ToString()); 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     Thread th1 = new Thread(Thread1); 
     th1.Start(); 

     for (int i = 0; i < 8; i++) 
     { 
      _queue.Enqueue(i); 
      _signal.Set(); 
     } 
    } 
+2

備考欄[ここ](https://msdn.microsoft.com/en-us/library/system.threading.eventwaithandle.set(v = vs.110))に「重要」の注記がありますか? aspx)?あなたの 'Set()'コールのほとんどが何もしていないと思われます。 –

+0

[BlockingCollection](https://msdn.microsoft.com/en-us/library/dd267312(英語))の独自のインプリメンテーションを構築しようとしているようです。 v = .110).aspx)を使用してください。 –

答えて

3

私はあなたがやろうとしているものを見ると、MarcGravellさんのコメント@正しいか、また、どのような@mariosangiorgioが言っていることは事実です。回避策としては、代わりにMonitor Wait/Pulseメカニズムを使用することができます。次のように試してみてください:

private readonly Queue<int> _queue = new Queue<int>(); 
    private readonly object _locker = new object(); 

    public void Thread1() 
    { 
     List<int> values = new List<int>(); 
     int lastInput; 
     StringBuilder sb = new StringBuilder(); 

     while (values.Count < 5) 
     { 
      lock (this._locker) 
      { 
       // wait until there is something in the queue 
       if (this._queue.Count == 0) 
       { 
        Monitor.Wait(this._locker); 
       } 

       // get the item from the queue 
       _queue.Dequeue(out lastInput); 

       // add the item to the list 
       values.Add(lastInput); 
      } 
     } 

     for (int i = 0; i < values.Count; i++) 
     { 
      sb.Append(String.Format("{0}\n", values[i])); 
     } 
     MessageBox.Show(sb.ToString()); 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     Thread th1 = new Thread(Thread1); 
     th1.Start(); 

     for (int i = 0; i < 8; i++) 
     { 
      lock (this._locker) 
      { 
       // put something in the queue 
       _queue.Enqueue(i); 

       // notify that there is something in the queue 
       Monitor.Pulse(this._locker); 
      } 
     } 
    } 

ですから、基本的には5つのアイテムを試してみることにします。コンシューマスレッドは、消費するキュー内のアイテムがないと判断した場合、プロデューサがキューにいくつかのアイテムを置くまで待機します。プロデューサがアイテムをキューに入れると、待ち状態の消費者スレッドに、準備が整ったことを通知します。その後、消費者スレッドは、キュー内にある可能性のあるアイテムをすべてブロックして消費します。あなたが@mariosangiorgioコメントを考慮すれば

は今さらに、あなたが実際に同時コレクションを使用しています。だから彼は正しいです、実際にブロックする必要はありません。だから、あなた自身のブロッキング/アンブロッキング実験をしたいのであれば、私の実装に行き、普通のQueue(非並行)を使うだけです。 @mariosangiorgioのように、AutoResetEventを削除し、ConcurrentQueueにそのことをさせてください。

が、あなたが実際のDequeue D「を取得し、あなたは意志連続ループを遮断し、何かするまでCPUを実行しない場合があることに注意してください。

0

私は問題があなたにあると思う_signal.Set();_signal.WaitOne();

これは、WaitOneが常にSetの前に呼び出されるインターリーブがある場合にのみ、必要な方法で動作します。あなたはAutoResetEventを使用する必要はありませんConcurrentQueueを使用しているので

_signal.WaitOne(); // This waits for the first set 
_signal.Set(); // This notifies the first `WaitOne` 
_signal.Set(); // This doesn't notify anything 
_signal.Set(); // This doesn't notify anything 
_signal.Set(); // This doesn't notify anything 
_signal.Set(); // This doesn't notify anything 
_signal.WaitOne(); // Nothing is going to set this 

:私はあなたのケースでは、あなたが次のイベントインターリーブを持っていると思われます。ただそれを削除し、すべてが動作するはずです。

+1

'AutoResetEvent'だけを削除すれば、何かがqueue'dになるまでループしてCPUを食べることはありませんか? – Snoopy

+0

はい、あなたが正しいよ – mariosangiorgio

+1

私がマルチスレッドについて最初に学んだとき、私はそのことをするために私に男の子を怒らせ、彼が言ったことを聞いていないと言った...私はそれを誇りにしていないそれは私と一緒に固執した... – Snoopy

1

私は問題の別のソリューションを提供しようと思いました。あなたは確かにAutoResetEventConcurrentQueueを使用することができますが、それはコードを理解しにくくして、正しく理解しています。

あなたは、一般的にシンプルな抽象化ライブラリを使用するようにしてください。私はMicrosoftの反応フレームワーク(NuGet "Rx-Main"、 "Rx-WinForms"、または "Rx-WPF")が好きです。

Rxはあなたが使用したい(スレッド)を使用すると、スケジューラを指定できる非同期で実行される操作のLINQのようなパイプラインを作成することができます。

これはあなたのコードと同等です:

IDisposable subscription = 
    Observable 
     .Range(0, 5, Scheduler.Default) 
     .ToArray() 
     .Select(xs => String.Join(Environment.NewLine, xs)) 
     .ObserveOn(this) 
     .Subscribe(x => MessageBox.Show(x)); 

Scheduler.Defaultの使用は、Windowsのアプリケーションのための新しいスレッドに計算をプッシュします。したがって、文字列の生成はUIスレッドから発生します。 .ObserveOn(this)は計算をUIに戻します(thisは現在のフォームを参照しています - thisの代わりにUI要素を使用できます)。

IDisposableです。したがって、長時間実行していて停止したい場合は、計算の短縮をいつでも中止するためにいつでもsubscription.Dispose()に電話することができます。

Rxライブラリは非常に強力で、非常に複雑な計算を比較的単純な形式で実行するための多くの演算子を提供します。

+1

また、Rxの大きなファン!私はあなたがRxについて私の質問のひとつで私を助けてくれたと思う。 – Snoopy

関連する問題