在退出之前等待完成孩子所有工作的演员

时间:2011-02-09 13:39:46

标签: scala actor

无法弄清楚如何解决以下问题:我有一些演员(工人)以某种方式执行任务,当他们收到(我的意思是反应)他们。主要演员(工头)控制这个过程并且可以接收任务以停止工作。在这种情况下,主要参与者必须停止创建新任务,并等待工人完成所有现有任务,然后才能退出主角色。

import actors.Actor
import actors.Actor._

class Foreman extends Actor{
  val workerA = new WorkerA
  val workerB = new WorkerB
  val workerC = new WorkerC
  self.link(workerA)
  self.link(workerB)
  self.link(workerC)
  def act{
    workerA.start
    workerB.start
    workerC.start

    // adding tasks to workers somehow
    //...
    loop{
      case ResultOfTask(res) => //...
      case Stop => //workers mustn't immediately stop but must finish their tasks and then exit
      case ProductionAccident => //...
    }


  }
}

case class Task(activity:String)
case class ResultOfTask(result:String)

trait Worker extends Actor{

  def act{
    loop{
      react{
        case Task(activity) => sender ! processTask(activity)
      }
    }
  }

  def processTask(activity:String):ResultOfTask
}

为了解决这个问题,我写了以下代码:

def condition = workerA.getState!=State.Suspended  && workerB.getState!=State.Suspended && workerC.getState!=State.Suspended && mailboxSize == 0
case Stop => {
  if(condition) exit("sweet dreams") else continue
}

检查主要演员是否应退出。在“工人”特征中具有反对者的另一种变体,当工人收到消息时递增它,并在它响应时递减。

trait Worker extends Actor {
  private var count = 0
  def act {
    loop{
      react{
        case Task(activity) => {
          count += 1
          sender ! processTask(activity)
          count -= 1
        }
      }
    }
  }
  def hasDoneAllTasks = count == 0

  def processTask(activity: String): ResultOfTask
}

“Foreman”中的“条件”功能将是

def condition = workerA.hasDoneAllTasks   && workerB.hasDoneAllTasks  && workerC.hasDoneAllTasks  && mailboxSize == 0

我希望有更好的解决方案,你会提出它们。

4 个答案:

答案 0 :(得分:1)

为什么在向工人发送工作时不包括对Foreman演员的引用?然后,当工人关闭时,他们可以向工头发送通知。每当工头收到工人关闭消息时,它都会记录它并查看是否所有工人都已完成。如果是这样,它也会自行关闭。

答案 1 :(得分:1)

我的方法是在Foreman中进行所有计算。

你没有写出工头如何创建任务,所以我认为它是对一条消息的回应

class Foreman extends Actor{

  var numTasks: Int = 0
  var shouldExit = false

  def act = loop {
   react {
     case t: Task =>
       if (!shouldExit)  {
         numTasks += 1
         selectWorker ! t
       } else {
         // send some kind of error status to sender
       }
     case ResultOfTask(rest) =>
       numTasks -= 1
       // ....
       if (numTasks == 0 && shouldExit) exit
     case Stop() => shoudExit = true
}

然后改进优先级,以便即使队列中有任务消息也可以处理它

  def act = loop {
   reactWithin(0) {
     case Stop() => shouldStop = true
     case TIMEOUT => react {
       case t: Task =>
         if (!shouldExit)  {
           numTasks += 1
           selectWorker ! t
         } else {
           // send some kind of error status to sender
         }
       case ResultOfTask(rest) =>
         numTasks -= 1
         // ....
         if (numTasks == 0 && shouldExit) exit
       case Stop() => shoudExit = true
  }
}

答案 2 :(得分:1)

如果工头总是希望得到工人的答案,解决方案很简单:工头维护一个计数器,每次发送一个消息,它就会递增,每次从工人那里收到一个,它就会减少。只要计数器为零,它就可以自行停止(假设没有其他人向工人发送信息)。

如果工头并不总是期望工人给出答案,你可以通过无内容的消息来表明这一点

case object Done { }

让工人在完成后回复。然后,见上文。

如果工头不是唯一一个与工人交谈的人,或者你希望在后台不那么喋喋不休,那么工头和工人将不得不进行谈判。领班可以发送

case object RequestStop { }

工作人员会做一些优雅的事情,并在完成后用Done回复。当工头收到Done次发送的RequestStop条消息时,可以自由退出。

答案 3 :(得分:1)

你可以利用的一件事是trapExit,case退出和退出系统。在您的主演员中,您可以将trapExit设置为true:

// Foreman

def act() {
  trapExit = true
  link(workerA)
  link(workerB)
  ...
}

这意味着当工人进程终止时,你的领班演员将收到一条退出消息,包括一个原因:

// Foreman

def act() {
  ....
  loop { react {
    case Exit (worker: Actor, reason: AnyRef) => {
      // decrement counter, or list of workers, and exit if empty
    }
    ...
   }}
}

您可以在reason参数上进行模式匹配。例如,在您的工人类中,您可以使用各种案例类退出,指出领班应该做什么:

// Worker:

exit(WorkComplete)
exit(Emergency)
等等,等等。当您的工作程序抛出异常时,它将终止并向链接的进程发送包含该异常的Exit消息。鉴于此类事情,您最终可能会遇到以下情况:

// Foreman

def act() {
  ....
  loop { react {
    case Exit (worker: Actor, reason: WorkComplete) => {
      // decrement counter, or list of workers, and exit if empty
    }

    case Exit (worker: Actor, reason: TasksExhausted) => {
      // log something, or make shut down worker if too many are exhausted
    }

    case Exit (worker: Actor, reason: Exception) => {
      // log the exception, then restart the actor
    }

    ...
   }}
}

从你最初的问题不清楚你是否希望工人继续工作,即使工作完成,直到工头告诉他们他们应该在时间退出。如果是这种情况,向工作人员发送消息告诉他们“完成时退出”,并且您可以通过使用trapExit机制告诉他们已经完成。

希望这会刺激一个有趣的解决方案!