从线程模型转换为actor

时间:2010-11-11 21:15:16

标签: scala concurrency refactoring actor

试图了解如何根据角色而不是线程来思考。我对以下用例感到有点难过:

  

考虑一个系统,该系统具有创建工作的生产者进程(例如,通过从文件读取数据),以及许多使用该工作的工作进程(例如,通过解析数据并将其写入数据库)。工作生产和消费的速度可能不同,系统应保持稳健。例如,如果工人无法跟上,生产者应该检测到这一点并最终减速或等待。

使用线程很容易实现:

val producer:Iterator[Work] = createProducer()
val queue = new LinkedBlockingQueue[Work](QUEUE_SIZE)
val workers = (0 until NUM_WORKERS) map { i =>
  new Thread() { 
    override def run() = {
      while (true) {
        try {
          // take next unit of work, waiting if necessary
          val work = queue.take()
          process(work)
        }
        catch {
          case e:InterruptedException => return
        }
      }
    }
  }
}

// start the workers
workers.foreach(_.start())

while (producer.hasNext) {
  val work = producer.next()
  // add new unit of work, waiting if necessary
  queue.put(work)
}

while (!queue.isEmpty) {
  // wait until queue is drained
  queue.wait()
}

// stop the workers
workers.foreach(_.interrupt())

这个型号没有什么问题,我以前成功使用过它。这个例子可能过于冗长,因为使用Executor或CompletionService可以很好地适应这个任务。但我喜欢演员抽象,并认为在许多情况下更容易推理。有没有办法使用actor重写这个例子,特别是确保没有缓冲区溢出(例如完整邮箱,丢弃的消息等)?

1 个答案:

答案 0 :(得分:3)

因为演员处理消息“离线”(即消息的消费与他们被接收的消息无关),所以很难看出你如何能够准确地模仿“生产者等待消费者赶上来”。

我唯一能想到的是消费者向生产者演员(使用reply)请求作品:

case object MoreWorkPlease
class Consumer(prod : Producer) extends Actor {
  def act = {
    prod ! MoreWorkPlease
    loop {
      react {
        case Work(payload) => doStuff(payload); reply(MoreWorkPlease)
      }
    }
  }
}

class Producer extends Actor {
  def act = loop {
    react {
      case MoreWorkPlease => reply(Work(getNextItem))
    }
  }
}

当然,这并不完美,因为制作人不会“向前阅读”,只有在消费者做好准备时才能开始工作。用法如下:

val prod = new Producer
(1 to NUM_ACTORS).map(new Consumer(prod)).foreach(_.start())
prod.start()