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. 为什么,如果我使用 notify 而不是 notifyAll,我最终会陷入僵局;我应该在哪里使用 notifyAll,用于生产或消费?

  2. 我不应该有一个对象,例如锁,然后调用 lock.synchronized、lock.wait 和 lock.notify 吗?为什么它会这样工作,不生产和消费有 2 个不同的监视器关联?为什么生产的“通知”通知消费的“等待”?

  3. 监视器在 scala 中是如何工作的(在我们的例子中)?它是否使用信号并继续策略?如何在特定条件下将等待队列中的进程移动到可运行队列?每个条件/锁是否有一个队列(例如 lock1.wait、lock2.wait 等)。

4

2 回答 2

5

这实际上是一个主要关于 Java 并发的问题。Scala 并发建立在 Java 并发模型之上,但语法不同。在 Scala 中,synchronized是 的一个方法AnyRef,上面的语法等价于使用关键字synchronized来编写同步方法,如下面的 Java 代码:

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 并发库。例如,您可以使用容量为 1的阻塞队列来实现相同的功能。

在回答您的问题时:

1. 为什么,如果我使用 notify 而不是 notifyAll,我最终会陷入僵局;我应该在哪里使用 notifyAll,用于生产或消费?

您可能有一个竞争条件(而不是死锁),因为notify()inconsumer()是由其他一些消费者线程而不是生产者线程接收的,但这只是一个猜测。至于是否使用notify()notifyAll()以何种方式使用,有人建议notifyAll()总是使用。而且,在这种情况下,您可以使用notifyAll(), 因为您在条件 while 循环中等待 - 由于wait()文档中描述的各种原因,您应该始终这样做。但是,您也可以选择在 中notify()用作优化producer(),因为我假设您只希望一个消费者使用缓冲区内容。使用当前的实现,您仍然必须使用notifyAll()inconsume()或者可能将自己暴露于通知其中一个消费者而不是单个等待的生产者导致生产者永远等待的情况。

2.我不应该有一个对象,例如锁,并调用lock.synchronized、lock.wait和lock.notify吗?为什么它会这样工作,不生产和消费有 2 个不同的监视器关联?为什么生产的“通知”通知消费的“等待”?

你确实有一把锁。这是对PC实例的隐式锁定,在 Java 中,每个对象只有一个监视器,尽管可能有许多入口点。wait()in由inconsume()通知,因为它们都在等待对同一资源(实例)的锁定。如果您想实现更灵活或更细粒度的锁定,则可以使用Java 并发库中的各种策略,例如Locksnotify()produce()PC

3. 监视器在 scala 中是如何工作的(在我们的例子中)?它是否使用信号并继续策略?如何在特定条件下将等待队列中的进程移动到可运行队列?每个条件/锁是否有一个队列(例如 lock1.wait、lock2.wait 等)。

有关 JVM 如何执行线程同步的详细说明,请阅读以下内容:Java 虚拟机如何执行线程同步。有关同一作者的一章的更多详细信息,您可以阅读Java 虚拟机内部

于 2013-02-25T22:18:02.880 回答
1

为什么,如果我使用 notify 而不是 notifyAll,我最终会陷入僵局?

正如您可能已经观察到的那样,如果只有一个生产者和一个消费者,则不会发生您所看到的问题(因为在这种情况下,notify您期望它做的工作是让下一个生产者/消费者采取行动)。

但是,如果您有多个生产者或消费者,则会出现以下问题:假设有 2 个生产者和一个消费者。在这种情况下,如果您使用 ,肯定会发生以下情况notify()

  1. 其中一位生产者运行,并调用notify()
  2. 代替消费者,通知另一个生产者
  3. 现在被唤醒的生产者notify()无限等待,因为消费者永远不会得到通知

如果notifyAll被调用,总会有一个消费者得到通知,因此永远不会出现生产者或消费者无限期等待对方的问题。

我不应该有一个对象,例如锁,然后调用 lock.synchronized、lock.wait 和 lock.notify 吗?

您的锁定对象就是PC对象。Scalaobject只是编译器为您生成的类的单例实例。由于 yourobject实际上是 class 的类实例Object,因此它也继承了它的notify,notifyAllwait方法。

于 2013-02-25T22:32:44.993 回答