为什么Scala中的生产者 - 消费者解决方案不起作用?

时间:2013-07-28 16:03:56

标签: scala actor producer-consumer

我目前正在通过“Scala编程”一书中学习Scala,第二版。我尝试使用actor实现生产者 - 消费者问题,但我的代码只打印:

Producer: new value: 0
Consumer #2: read value: 0
Consumer #4: read value: 0
Consumer #1: read value: 0
Consumer #3: read value: 0
Consumer #5: read value: 0

然后它挂起。我无法弄清楚我做错了什么。 任何帮助非常感谢!

Producer.scala:

package cz.zaoral.scala.actors.prodcons

import scala.actors.Actor
import scala.actors.Actor.{react, actor, self}

class Producer(
  val cycles: Int,
  val productionTime: Int,
  val consumers: List[Actor])
extends Actor {
  private var consumersConsumed = 0
  private var currentValue = 0

  case class ProduceNewValue(cycleNum: Int)
  case class NewValueProduced(value: Int)

  private val producer = actor {
    loop {
      self.react {
        case ProduceNewValue(x) =>
          Thread.sleep(productionTime)
          Producer.this ! NewValueProduced(x)
        case x => println("Unhandled message in producer: "+ x)
      }
    }
  }

  def act() {
    producer ! ProduceNewValue(currentValue)
    loop {
      if (consumersConsumed == consumers.length - 1) {
        consumersConsumed = 0
        if (currentValue < cycles) {
          currentValue += 1
          producer ! ProduceNewValue(currentValue)
        } else {
          sendToAllConsumers(EndOfInput())
          exit()
        }
      }
      receiveMessage()
    }
  }

  private def receiveMessage() {
    react {
      case ValueRead() => consumersConsumed += 1
      case NewValueProduced(x) =>
        println("Producer: new value: "+ x)
        sendToAllConsumers(ValueProduced(x, self))
      case msg => println("unhandled message in Producer: "+ msg)
    }
  }

  private def sendToAllConsumers(msg: Message) {
    for (consumer <- consumers)
      consumer ! msg
  }
}

Consumer.scala

package cz.zaoral.scala.actors.prodcons

import scala.actors.Actor

class Consumer(number: Int) extends Actor {
  def act() {
    loop {
      react {
        case ValueProduced(x, producer) =>
          println(this + ": read value: "+ x)
          producer ! ValueRead()
        case EndOfInput => exit()
        case x => println("Unhandled message in Consumer: "+ x)
      }
    }
  }

  override def toString =
    "Consumer #"+ number

  start()
}

Messages.scala:

package cz.zaoral.scala.actors.prodcons

import scala.actors.Actor

abstract class Message

case class ValueProduced(newValue: Int, producer: Actor) extends Message
case class ValueRead extends Message
case class EndOfInput extends Message

Demo.scala

package cz.zaoral.scala.actors.prodcons

import scala.actors.Actor

object Demo {
  def main(args: Array[String]) {
    val consumers = for {
      i <- 1 to 5
      consumer = new Consumer(i)
    } yield consumer

    val producer = new Producer(10, 1000, consumers.toList)
    producer.start()
  }
}

修改

我自己找到了解决方案:)我以错误的方式使用了actor方法。 scala.actors.Actor.actor方法是一个工厂方法,可以创建并立即启动新的actor。

New Producer.scala:

package cz.zaoral.scala.actors.prodcons

import scala.actors.Actor
import scala.actors.Actor.{react, actor, self}


class Producer(
  val cycles: Int,
  val productionTime: Int,
  val consumers: List[Actor])
extends Actor {
  private var remainingConsumers = consumers.toSet
  private var currentValue = 0

  case class NewValueProduced(value: Int)

  private def produce() = actor {
    Thread.sleep(productionTime)
    Producer.this ! NewValueProduced(currentValue + 1)
  }

  def act() {
    produce()
    loop {
      if (remainingConsumers.isEmpty) {
        if (currentValue < cycles) {
          remainingConsumers ++= consumers
          produce()
        } else {
          sendToAllConsumers(EndOfInput())
          exit()
        }
      }
      receiveMessage()
    }
  }

  private def receiveMessage() {
    react {
      case ValueRead(consumer) =>
        assert(remainingConsumers.contains(consumer))
        remainingConsumers -= consumer
      case NewValueProduced(x) =>
        currentValue = x
        println("Producer: new value: "+ currentValue)
        sendToAllConsumers(ValueProduced(currentValue, self))
    }
  }

  private def sendToAllConsumers(msg: Message) {
    for (consumer <- consumers)
      consumer ! msg
  }
}

请注意,如果有人希望在示例的其余部分中使用这个新定义,则必须在其他文件中进行一些小的更改,但这不应该是一个大问题。

美好的一天!

0 个答案:

没有答案