2

私は単純な生産者/消費者シナリオを持っており、生産/消費されているアイテムは1つだけです。また、プロデューサーはワーカースレッドが終了するのを待ってから続行します。この種のマルチスレッド化のポイント全体が不要になることは理解していますが、実際にはこのようにする必要があると想定してください(:

このコードはコンパイルされませんが、次のようなアイデアが得られることを願っています。

// m_data is initially null

// This could be called by any number of producer threads simultaneously
void SetData(object foo)
{
    lock(x)                      // Line A
    {
        assert(m_data == null);
        m_data = foo;
        Monitor.Pulse(x)         // Line B
        while(m_data != null)
            Monitor.Wait(x)      // Line C
    }
}

// This is only ever called by a single worker thread
void UseData()
{
    lock(x)                      // Line D
    {
        while(m_data == null)
            Monitor.Wait(x)      // Line E
        // here, do something with m_data
        m_data = null;
        Monitor.Pulse(x)         // Line F
    }
}

これが私がよくわからない状況です:

多くのスレッドが異なる入力でSetData()を呼び出すと仮定します。そのうちの1つだけがロック内に入り、残りはラインAでブロックされます。ロック内に入ったものがm_dataを設定し、ラインCに到達するとします。

質問:行CのWait()は、行Aの別のスレッドがロックを取得し、ワーカースレッドがそれに到達する前にm_dataを上書きできるようにすることができますか?

それが起こらず、ワーカースレッドが元のm_dataを処理し、最終的にラインFに到達すると仮定すると、そのPulse()がオフになるとどうなりますか?

ラインCで待機しているスレッドのみがロックを取得できますか?それとも、ラインAで待機している他のすべてのスレッドとも競合しますか?

基本的に、Pulse()/ Wait()が特別に「内部」で相互に通信するのか、それともlock()と同じレベルにあるのかを知りたいです。

もちろん、これらの問題の解決策は明らかです。SetData()を別のロック(たとえば、lock(y)で囲むだけです)。そもそも問題なのか気になります。

4

1 に答える 1

10

コンシューマーが別のプロデューサーの前に待機キューまたは準備完了キューにエンキューされるという保証はありません。
C#およびJavaスタイルのモニターについては、ウィキペディアの「暗黙の状態モニター」で説明されています。
何が起こっているのかについての素晴らしい概要Monitorこの素晴らしいサイトから引用): 代替テキスト

「行CのWait()により、行Aの別のスレッドがロックを取得し、ワーカースレッドがロックに到達する前にm_dataを上書きできるようになりますか?」

これが2つのプロデューサースレッドP1P2SetData()によって呼び出されたとします。 コンシューマースレッドであるC1も開始されます。P1P2、およびC1はすべてレディキューに入ります。P1が最初にロックを取得します。 待機キューは空です。オンしても効果はありません。P1は待機しているため、待機キューに入れられます。 レディキューの次のスレッドがロックを取得します。 同様にP2またはC1にすることができます-最初のケースでは、アサーションは失敗します。 競合状態があります。



Pulse()line B
line C


「それが起こらず、ワーカースレッドが元のm_dataを処理し、最終的にラインFに到達すると仮定すると、そのPulse()がオフになるとどうなりますか?」

ウェイターを待機キューから準備キューに移動します。
ロックは、を発行するスレッドによって保持されますPulse()
通知されたスレッドは、パルススレッドがロックを解放した後にロックを取得する機会を取得します(準備完了キューにはすでに他のスレッドが存在する可能性があります)。
MSDNから、Monitor.Pulse()
"指定されたオブジェクトのロックを現在所有しているスレッドは、このメソッドを呼び出して、ロックの次のスレッドに信号を送ります。パルスを受信すると、待機中のスレッドは準備完了キューに移動されます。パルスを呼び出したスレッドがロックを解放すると、レディキュー内の次のスレッド(必ずしもパルスされたスレッドである必要はありません)がロックを取得します。」

「ラインCで待機しているスレッドだけがロックを取得できますか?それとも、ラインAで待機している他のすべてのスレッドと競合しますか?」

レディキューにあるすべてのスレッドは、次にロックを取得するために「競合」します。
彼らがまっすぐそこに着いたのか、それとも。を使って待ち行列から来たのかは関係ありませんPulse()

「キュー」は他の方法で実装される場合があります。(キューのデータ構造自体ではありません)。
このように、Monitor実装は公平性を保証しない場合がありますが、全体的なスループット/パフォーマンスが高くなる可能性があります。

于 2010-04-13T17:28:29.933 に答える