13

CountdownEventを使用して、イベントのカウントがゼロの場合にのみスレッドを続行できるようにしようとしていますが、初期カウントをゼロにしたいと考えています。実際には、カウントがゼロのときはいつでもイベントが通知され、ゼロより大きいときはいつでもスレッドが待機するように、ゼロに戻る動作が必要です。

0 の初期カウントで Countdown イベントを初期化できますが、カウントに追加しようとするとInvalidOperationException "CountdownEvent_Increment_AlreadyZero" が発生します。

この制限を回避するために Countdown イベントを使用できる代替クラスまたは別の方法はありますか?

4

8 に答える 8

7

編集

public void Dispatch()
{
    using (var ev = new CountdownEvent(1))
    {
        foreach (var task in <collection_of_tasks_to_start>)
        {
            ev.AddCount();
            // start *task* here. Don't forget to pass *ev* to it!
        }

        ev.Signal();
        ev.Wait();
    }
}

// task code
void Handler(CountdownEvent ev)
{
    try
    {
        // do task logic
    }
    finally
    {
        ev.Signal();
    }
}

なぜ、どのようにこれが機能するのですか?

  1. タスクを生成していない場合
    • 何も起こらず、Dispatch正常に終了します。
    • ev.Signal一度呼び出されると、カウンターの初期値が実行をブロックしないことを確認ev.Waitします0Wait
  2. 少なくとも 1 つのタスクを生成した場合
    • 高速実行タスクと低速実行タスクの 2 つのケースを考慮し、それらev.Wait()が呼び出された時点での実行にどのように影響するかを理解します。
      • 高速なタスク、つまり、最後に到達する前に完了するタスク。
        • 状況でev.Wait()ポイント 1 に相当します。
      • 遅いタスク、つまり最後に到達する前に完了していないタスク。
        • 実行されたすべてのために、カウンターはもう等しくev.Wait()ありません。したがって、実行は保持されます。0ev.AddCount()
        • 実行中のすべてのタスクが完了する (および対応するev.Signal()行が実行される) と、カウンターは に下がり0、実行が再開され、ルーチンを終了します。

元の答え

あなたが書いた:

不明な数の子操作 (タスクまたはスレッドではない) を作成する操作を実行しています

それで、彼らは何ですか?次のようにする必要があります。

CountdownEvent ev;  
public void foo() {
    ev = 新しい CountdownEvent(1);
    foreach ( <tasks_to_start のタスク> ) {
         ev.AddCount();
         // タスクを開始するコードをここに入力します
    }
    ev.シグナル();
    ev.Wait();
}

public static void youtTask(CountdownEvent ev) {
    // いくつかの作業
    // ...
    // すべてが終わったら
    ev.シグナル();
}
于 2011-01-03T01:32:13.143 に答える
3

.NET 4.0 または.NET 3.5 用のReactive Extensions (.NET 4 TPL 機能のバックポートがある) を使用できる場合は、Barrierクラスを確認してください。複数の並列タスクを調整して、バリア内のすべての参加者が到着を知らせるまで続行しないようにすることができます。また、処理中に参加者を表示および非表示にするという要件も満たす必要があります。

于 2010-12-08T18:48:30.763 に答える
2

したがって、本質的には、任意のカウントダウンで設定できる同期オブジェクトではなく、「オン/オフ スイッチ」が必要です。CountdownEventこのような場合には適していません。

Semaphore初期カウントを 1 にしてa を使用しないのはなぜですか。

于 2010-12-08T13:50:07.840 に答える
1

あなたの質問は、一般的なツリー ウォーク フォーク テクニックのようです。再帰するたびに、代わりに別の同時操作を開始します (スレッドプールにエンキューするなど)。ただし、最後にすべてのサブブランチが終了するまで待つ必要があります。開始する各サブ操作のカウントダウン イベントに 1 を追加し、各サブ操作の終了時にそれを知らせるだけです。子操作ごとに追加するまでシグナルを送信しないようにアルゴリズムを調整する限り、安全に実行できます。

前もってカウントを知る必要はなく、ルートで1にするだけで、子にフォークするたびに1を追加し、それぞれの最後にシグナルを送ることを追加する必要があります。動的に初期費用なしであらゆるツリーを処理します。

CountdownEvent には Add メソッドがあり、飛行中にカウントを増やすことができます。

それは理にかなっていますか?私はあなたが達成しようとしていることからは程遠いかもしれません。

ただし、指定したとおりに動作する CountdownEvent が本当に必要な場合は、いくつかのインターロックされた操作をクラスにラップして、指定したことを実行するのは非常に簡単です。

ただし、CountdownEvent は羽のように軽量に構築されており、信号が送られる前に誰も待たなければほとんど無料です。高価なケースでは、最悪のケースでは、信号へのカーネル遷移と待機へのカーネル遷移を 1 つだけ行う必要があるタスク (など) の数に関係なく、最適です。

あなたが提案したものを実装するには、イベントのシグナリングとリセットに関する同期が必要です。カウントダウン イベントは 1 つの単純な原則に依存しています。Signal 呼び出しで非ゼロからゼロへの遷移のみがイベントを通知できる可能性があります。複数のスレッドが一度に値を変更することはできない (インターロックされている) ため、競合は発生しません。そのため、1 つのスレッドのみがイベント オブジェクトにシグナルを送ろうとすることができます (これにより、他の待機中のスレッドが目覚めます)。完全。

ただし、複数のスレッドで設定とリセットを行う場合は、設定とリセットを同期する必要があります。これは、カウントが数回変動する可能性があり、複数のスレッドがすべて同時にイベントを設定またはリセットしようとするためです。(イベントの設定、リセット、および待機はすべて、カーネルの移行を行い、コンテキスト スイッチを発生させる必要があるため、すべてコストがかかります)。セット/リセット遷移を保護するために何かを同期しない限り、機能しません。これを CountdownEvent に追加すると、ほぼ最適ではなくなり、大幅にコストが高くなります。

于 2012-12-18T03:01:34.960 に答える
1

これはうまくいきますか? http://msdn.microsoft.com/en-us/library/dd384749.aspx

編集
あいまいで申し訳ありません。各親に 1 から始まるカウントダウン イベントがある SOReader の回答を使用します。親スレッドの親。ツリーのような一連のカウントダウン イベントです。

私はマルチスレッドの経験がありませんが、一見したところ、それが私が試みるものです。

于 2011-04-15T20:14:55.417 に答える
0

Queue オブジェクトを使用して、「作業」を追加して再度取り出すことができます。

Queue が空になったときだけ先に進みます。

しかし、ええ、ここで詳細が必要になります...

于 2011-03-15T11:28:08.540 に答える
0

セマフォはどうですか: http://msdn.microsoft.com/en-us/library/system.threading.semaphore.aspx

編集: 次の投稿では、説明した内容が推奨されない理由について説明し、回避策も提案しています: http://social.msdn.microsoft.com/Forums/en/parallelextensions/thread/aa49f92c-01a8-4901-9846-91bc1587f3ae

于 2010-12-08T13:47:58.700 に答える