1
private ConcurrentQueue<Data> _queue = new ConcurrentQueue<Data>();
private AutoResetEvent _queueNotifier = new AutoResetEvent(false);

public void MoreData(Data example)
{
    _queue.Enqueue(example);
    _queueNotifier.Set();
}

private void _SimpleThreadWorker()
{
    while (_socket.Connected)
    {
        _queueNotifier.WaitOne();
        Data data;
        if (_queue.TryDequeue(out data))
        {
            //handle the data
        }
    }
}

イベントをデキューしたら、イベントを false に設定する必要がありますか?それとも、イベントが戻ってきたとき、_queueNotifier.WaitOne()または正確にどのように機能するかによって、それ自体で false に戻りますか?

代わりに以下の例のようにインナー while を使用する必要がありますか、それとも両方の方法で問題ありませんか。

while (_socket.Connected)
{
    _queueNotifier.WaitOne();
    while (!_queue.IsEmpty)
    {
        Data data;
        if (_queue.TryDequeue(out data))
        {
            //handle the data
        }
    }
}
4

2 に答える 2

4

ConcurrentQueue.NET 4 から使用している場合は、AutoResetEvent自分で処理を完全に行わないようにすることをお勧めします。代わりに、BlockingCollectionをラップする を作成し、ConcurrentQueueそれを使用するだけで、必要なすべてのことを実行できます。(BlockingCollectionパラメータなしのコンストラクタを使用して を作成するだけConcurrentQueueでも、とにかく が作成されます。)

編集: 本当にまだ を使用したい場合はAutoResetEventWaitOneイベントを自動的に (そしてアトミックに) リセットします。これは の「自動」部分ですAutoResetEvent。これを、イベントをリセットしないものと比較してくださいManualResetEvent

于 2011-07-19T09:05:18.883 に答える
1

あなたが行うと_queueNotifier.Set()、イベントはシグナル状態になります。イベントが通知_queueNotifier.WaitOne()され、他のスレッドから呼び出されると、次の 2 つのことが同時に発生します (つまり、カーネル モードで)。

  • イベントは非通知になります (自動リセットされるため)
  • スレッド呼び出しのWaitOneブロックが解除されました

そのため、イベント ステータスを自分で明示的に設定する必要はありません。

ただし、Jon が言うように、共有変数を使ってキューからアイテムをプッシュおよびプルするだけの場合は、単純に a を使用するBlockingCollection方が便利です。

複数の共有変数にアクセスしている場合は、その周りに単一のスレッド同期メカニズム (独自の) を用意するのが理にかなっています。

また、独自のコードを使用する場合は、次のようにする方がよいと思われます。

public void MoreData(Data example)
{
    var queueWasEmpty = _queue.IsEmpty;
    _queue.Enqueue(example);
    if (queueWasEmpty) {
        _queueNotifier.Set();
    }
}

private void _SimpleThreadWorker()
{
    while (_socket.Connected)
    {
        _queueNotifier.WaitOne();
        Data data;
        while(!queue.IsEmpty) {
            if (_queue.TryDequeue(out data))
            {
                //handle the data
            }
        }
    }
}

このようにすると、コンシューマ関数がビジー状態のときに項目がキューに追加されるたびに (イベントを設定するために) カーネル モードに入る必要がなくなります。また、コンシューマー関数の各反復でカーネル モードに移行することも避けます。

後者に関しては、コンテキスト スイッチを回避するには_queue.IsEmptyテストを追加するという代償が伴うことは事実です (いずれにせよ、悪い実装ではコンテキスト スイッチが実行される可能性があります)。ただし、これはおそらく、カーネル モードに入る必要のないインターロックされた比較交換操作として実装されています。

免責事項:ソース コードまたは IL をチェックして、上記の情報を確認していません。

于 2011-07-19T09:13:35.830 に答える