2011-03-24 5 views
4

私は、複数のスレッドが待つことができるAutoResetEventのequivelantを望んでいます。設定された時点ですべてを再開したいと考えています。1つのイベントで複数のスレッドが待機していますか?

スレッドごとに1つずつ設定し、それぞれを設定することでこれが実現できることは分かっていますが、簡単な方法はありますか?イベントハンドルの配列に依存しない方法ですか?

(と思う)私がしたいが、これを行うことができるようにすることです効果的に何

private volatile string state; 
private MultiEventHandle stateChanged = new MultiEventHandle(); 

public void WaitForBlob() 
{ 
    while (true) 
    { 
    object saved = stateChanged.Current; // some sentinel value 
    if (state == "Blob") break; 
    stateChanged.WaitTilNot(saved); // wait til sentinel value != "current" 
    } 
} 

public void SetBlob() 
{ 
    state = "Blob"; 
    stateChanged.Change(); // stateChanged.Current becomes a new sentinel object 
} 

すなわち、任意の数のスレッドがWaitForBlobを呼び出すことはできませんし、いつでも(競合条件を)SetBlobすることができます待機中のすべてのスレッドがすぐに変更を検出します。重要なのは、スピンロックやThreading.Sleepsがないことです。

今私は "MultiEventHandle"を比較的簡単に実装できると思います。しかし、私の質問は...良い方法があるのですか?確かに私はこれがかなり一般的な使用例でなければならないので、間違っているつもりですが、私は仕事のための内蔵ツールを見つけることができないようです。私はここで四角い車輪を発明しようとしているのではないかと恐れています。

答えて

2

私はMonitor.PulseAllは/(プロセスにおけるモニタクラスについて少し学習)舞台裏で待って使用して「WatchedVariable」クラスに可能な解決策を包んました。他の誰かが同じ問題に遭遇した場合のためにここに投稿してください。不変のデータ構造を使っているかもしれません。 Jon Skeetに助けてくれてありがとう。

使用法:

private WatchedVariable<string> state; 

public void WaitForBlob() 
{ 
    string value = state.Value; 
    while (value != "Blob") 
    { 
    value = state.WaitForChange(value); 
    } 
} 

実装:

それは、私のテストケースに合格したご自身の責任で使用していますけれども
public class WatchedVariable<T> 
    where T : class 
{ 
    private volatile T value; 
    private object valueLock = new object(); 

    public T Value 
    { 
     get { return value; } 
     set 
     { 
      lock (valueLock) 
      { 
       this.value = value; 
       Monitor.PulseAll(valueLock); // all waiting threads will resume once we release valueLock 
      } 
     } 
    } 

    public T WaitForChange(T fromValue) 
    { 
     lock (valueLock) 
     { 
      while (true) 
      { 
       T nextValue = value; 
       if (nextValue != fromValue) return nextValue; // no race condition here: PulseAll can only be reached once we hit Wait() 
       Monitor.Wait(valueLock); // wait for a changed pulse 
      } 
     } 
    } 

    public WatchedVariable(T initValue) 
    { 
     value = initValue; 
    } 
} 

。私は受け入れるようになってる答えているかを把握するためにメタを相談する今

..

2

ManualResetEventを使用しない理由は何ですか?待機中のスレッドが1つもなくなってもリセットされないので、すべて解放されます。

もちろん、待ちスレッドがすべて終了した後にイベントResetが必要な場合は、それを検出する方法が必要です。 の代わりにSemaphoreを使用しても構いませんが、それは複雑であると思われます。

はあなたがあなたのケースでは、それを設定した後にイベントimmediatleyをリセットする必要がありますか?

+0

実際の使用例では、ブロックするいくつかの機能があり、状態が自分の望むものになるのを待っています。 WaitForBlob、WaitForSusanなどがあります。私が望むのは、グローバルな状態が変わるたびに、待っているすべてのスレッドが状態を再評価して、今すぐ使えるかどうかを確認することです。私はManualResetEventsをどうやってそれを曲げることができたのか考えていません。 – Mania

+0

詳細については、Eric LippertのC#での不変性のおかげで起こったものです。私は不変のAVLTreeのデータのヒープを格納している。時にはデバイスから読み込むのを待つときに、その一部が「ロック」されることがあります。今私はこの "ロックされた"セグメントを取得したい場合、最新の値を返す前に応答を待たなければなりません。だから私がする必要があるのは、ツリーが変更されるのを待つことです。その部分がまだロックされている場合は、ツリーがロックされていないときまで待機し続けます。複数のスレッドは、これをきれいに、できればスピンしない/小さなスリープなしで行うことができる必要があります。 – Mania

+0

@Mania:もしあなたがAutoResetEventを使っているコードを持っていれば、ManualResetEventはまったく同じです。最初のスレッドが管理するときには、それ自体はリセットされません。考えられるもう1つのオプションは、イベントを使用することです。各イベントハンドラは、適切なスレッドがWait onと呼んだモニタにパルスを送ります。このような低レベルの概念を使用することは、いくつかの点で悪い考えであるように思えます... .NET 4を使用していますか?もしそうなら、TPLの何かが助けになるかもしれません。 –

0

私は、この問題に対する別の解決策を考え出しました。 これは、イベントのために待機しているスレッドがWaitOne()通話にタイトなループではないと仮定して、待機コールの間にいくつかの作業があります。他のスレッドがイベントを待機しなくなるまで それが1 AutoResetEventを使用して、連続的にWaitOne(0)Set()を呼び出します。

// the only event we'll use: 
AutoResetEvent are = new AutoResetEvent(false); 
// starting threads: 
for (int i = 0; i < 10; i++) 
{ 
    string name = "T" + i; 
    new Thread(() => { while (true) { are.WaitOne(); WriteLine(name); } }).Start(); 
} 

// release all threads and continue: 
while (!are.WaitOne(0)) 
    are.Set(); 

上記のコードは、1000件のスレッドのためにテストされている間に少しより多くの仕事があるときに容易にゼロに制限することができますwhileループ、上あまりにも多くの反復のオーバーヘッドがありますが、それはそれらすべてを(リリースしましたスレッド内の呼び出しを待ちます。

ドキュメントからわからないことは、後で同じスレッドで呼び出されたWaitOne()をSet()が解放できるかどうかです。この状況が可能であれば、この解決策は安全ではありませんwhileループを終了する前にすべてのスレッドを解放しない可能性があるため使用します。 誰かがそれに光を当てることができればいいと思います。

関連する問題