6

scala での生産者と消費者の問題の次の実装が与えられた場合

class PC {
  var buffer = null.asInstanceOf[Int]
  var set = false

  def produce(value: Int) = synchronized {
    while (set) wait()
    buffer = value
    set = true
    notify() }

  def consume: Int = synchronized {
    while (!set) wait()
    val result = buffer
    set = false
    notify()
    return result; }
}

私が見ることができない3つのことがあります:

  1. なぜ、notifyAll の代わりに notify を使用すると、デッドロックに陥ってしまうのでしょうか。生産または消費のために、どこでnotifyAllを使用する必要がありますか?

  2. lock などのオブジェクトを用意して、lock.synchronized、lock.wait、および lock.notify を呼び出すべきではありませんか? 2 つの異なるモニターを関連付けて生成および消費しないのはなぜですか? 生産からの「通知」が消費からの「待機」通知を行うのはなぜですか?

  3. モニターは scala で正確にどのように機能しますか (私たちの場合)? シグナルアンドコンティニューポリシーを使用していますか? 特定の条件で待機中のキューからプロセスを実行可能なキューに移動するにはどうすればよいですか? 条件/ロックごとにキューがありますか (lock1.wait、lock2.wait など)。

4

2 に答える 2

5

これは、主に Java の並行性に関する質問です。Scala 並行性は Java 並行性モデルの上に構築されていますが、構文が異なります。Scala では、synchronizedは のメソッドでAnyRefあり、上記の構文は、キーワードを使用して次の Java コードのように同期メソッドsynchronizedを記述することと同じです。

public class PC {
  public int buffer;
  public boolean set;
  public synchronized void produce(int value) { 
    while(set) wait();
    buffer = value;
    set = true;
    notify(); 
  }
  public synchronized int def consume { 
    while(!set) wait();
    int result = buffer;
    notify();
    return result; 
  }
}

Java 並行性モデルの詳細については、Java チュートリアルを参照してください。Java Concurrency Libraryを調査することをお勧めします。たとえば、容量が 1のブロッキング キューを使用して同じことを実装できます。

あなたの質問に答えて:

1. notifyAll の代わりに notify を使用すると、デッドロックに陥ります。生産または消費のために、どこでnotifyAllを使用する必要がありますか?

プロデューサー スレッドではなく、他のコンシューマー スレッドによって受信されるため、(デッドロックではなく) 競合状態が発生している可能性がありますが、それは単なる推測notify()です。orconsumer()を使用するかどうか、およびどのメソッドで使用するかについては、常に使用することを推奨する人もいます。そして、この場合、条件付き while ループで待機しているため、 を使用できます。これは、 wait()のドキュメントで説明されているさまざまな理由で常に実行する必要があります。ただし、の最適化としてを使用することもできます。これは、1 つのコンシューマーのみがバッファーの内容を消費することを想定しているためです。現在の実装では、引き続き使用する必要がありますnotify()notifyAll()notifyAll()notifyAll()notify()producer()notifyAll()consume()または、単一の待機中のプロデューサーではなく、コンシューマーの 1 つに通知され、その結果、プロデューサーが永遠に待機する状況にさらされる可能性があります。

2. lock などのオブジェクトを持っていて、lock.synchronized、lock.wait、および lock.notify を呼び出すべきではありませんか? 2 つの異なるモニターを関連付けて生成および消費しないのはなぜですか? 生産からの「通知」が消費からの「待機」通知を行うのはなぜですか?

あなたはロックを持っています。これはインスタンスに対する暗黙のロックでPCあり、Java ではオブジェクトごとに 1 つだけのモニターがありますが、多数のエントリ ポイントが存在する可能性があります。wait()inはinconsume()によって通知されます。これは、どちらも同じリソース (インスタンス) のロックを待機しているためです。より柔軟できめ細かなロックを実装する場合は、LocksなどのJava Concurrency Libraryのさまざまな戦略を使用できます。notify()produce()PC

3. モニターは scala で正確にどのように機能しますか (私たちの場合)? シグナルアンドコンティニューポリシーを使用していますか? 特定の条件で待機中のキューからプロセスを実行可能なキューに移動するにはどうすればよいですか? 条件/ロックごとにキューがありますか (lock1.wait、lock2.wait など)。

JVM がスレッド同期を実行する方法の詳細については、Java 仮想マシンがスレッド同期を実行する方法 を参照してください。同じ著者の章の詳細については、Inside the Java Virtual Machineを参照してください。

于 2013-02-25T22:18:02.880 に答える
1

なぜ、notifyAll の代わりに notify を使用すると、デッドロックに陥るのですか?

お気づきかもしれませんが、1 つのプロデューサーと 1 つのコンシューマーしかない場合、あなたが目撃している問題は発生しません (その場合、notifyあなたが期待する仕事、つまり次のプロデューサー/コンシューマーに移動させるためです) )。

ただし、複数のプロデューサーまたはコンシューマーがある場合、次の問題が発生します。2 つのプロデューサーと 1 つのコンシューマーがあるとします。このシナリオで を使用すると、次のことが起こりますnotify()

  1. プロデューサーの 1 つが実行され、呼び出しますnotify()
  2. コンシューマーの代わりに、他のプロデューサーに通知されます
  3. notify()コンシューマーには通知が届かないため、今までに目覚めたプロデューサーは無限に待機します。

代わりに が呼び出された場合notifyAll、通知を受けるコンシューマが常に存在するため、プロデューサまたはコンシューマが相手を無期限に待機するという問題は決して発生しません。

lock などのオブジェクトを用意して、lock.synchronized、lock.wait、および lock.notify を呼び出すべきではありませんか?

あなたのロックオブジェクトはPCオブジェクトです。Scalaobjectは、コンパイラが生成するクラスのシングルトン インスタンスです。yourobjectは実際には class のクラス インスタンスであるため、その,およびメソッドObjectも継承します。notifynotifyAllwait

于 2013-02-25T22:32:44.993 に答える