2

私はMSDNでAutoResetEventのドキュメントを読んでいて、警告に従うとちょっと気になります。

「重要:Setメソッドへのすべての呼び出しがスレッドを解放するという保証はありません。2つの呼び出しが近すぎるため、スレッドが解放される前に2番目の呼び出しが発生すると、1つのスレッドのみが解放されます。まるで2番目の呼び出しは発生しませんでした。また、待機中のスレッドがなく、AutoResetEventがすでに通知されているときにSetが呼び出された場合、呼び出しは効果がありません。」

しかし、この警告は基本的に、そのようなスレッド同期技術を使用する理由そのものを殺します。たとえば、私は仕事を保持するリストを持っています。そして、リストにジョブを追加するプロデューサーは1人だけです。私には消費者(複数)がいて、リストから仕事を得るのを待っています..このようなもの..

プロデューサー:

void AddJob(Job j)
{
    lock(qLock)
    {
        jobQ.Enqueue(j);
    }

    newJobEvent.Set(); // newJobEvent is AutoResetEvent
}

消費者

void Run()
{
    while(canRun)
    {
        newJobEvent.WaitOne();

        IJob job = null;

        lock(qLock)
        {
            job = jobQ.Dequeue();
        }

        // process job
    }
}

上記の警告が当てはまる場合、2つのジョブを非常にすばやくキューに入れると、1つのスレッドだけがジョブを取得しますね。私は、Setがアトミックである、つまり次のことを行うと想定していました。

  1. イベントを設定する
  2. スレッドが待機している場合は、1つのスレッドを選択してウェイクアップします
  3. イベントをリセットする
  4. 選択したスレッドを実行します。

だから私は基本的にMSDNの警告について混乱しています。有効な警告ですか?

4

2 に答える 2

2

MSDNのメッセージは確かに有効なメッセージです。内部で起こっていることは次のようなものです:

  1. スレッドAはイベントを待機します
  2. スレッドBはイベントを設定します
  3. [スレッドAがスピンロック状態の場合]
    1. [はい]スレッドaは、イベントが設定されていることを検出し、設定を解除して、作業を再開します
    2. [no]イベントは、スレッドAにウェイクアップするように指示します。ウェイクアップすると、スレッドAはイベントの設定を解除して作業を再開します。

スレッドBはスレッドAがビジネスを継続するのを待たないため、内部ロジックは同期していないことに注意してください。これを同期させるには、一時的なManualResetEventを導入して、スレッドAが作業を続行したときに通知する必要があり、スレッドBが待機する必要があることを通知します。これは、Windowsスレッドモデルの内部動作のため、デフォルトでは実行されません。ドキュメントは誤解を招くと思いますが、Setメソッドは1つ以上の待機中のスレッドのみを解放すると言っているのは正しいです。

または、.NET 4.0で導入されたBCLのSystem.Collections.Concurrent名前空間のBlockingCollectionクラスを確認することをお勧めします。これは、まさにあなたがやろうとしていることを実行します。

于 2012-02-29T05:22:16.613 に答える
2

警告が正しくなく、Set がアトミックである場合でも、なぜここで AutoResetEvent を使用するのでしょうか? いくつかのプロデューサーが 3 つのイベントを連続してキューに入れ、1 つのコンシューマーがいるとします。2 番目のジョブを処理した後、コンシューマーはブロックし、3 番目のジョブを処理しません。

このタイプの同期にはReaderWriterLockSlimを使用します。基本的に、複数のプロデューサが書き込みロックを保持できるようにする必要がありますが、コンシューマがキュー サイズのみを読み取っている間、プロデューサを長時間ロックアウトすることは望ましくありません。

于 2012-02-29T05:21:33.083 に答える