Актер, который ждет завершения всех заданий у детей перед выходом - PullRequest
3 голосов
/ 09 февраля 2011

Не могу понять, как решить следующую проблему: у меня есть несколько действующих лиц (работников), которые каким-то образом выполняют задачи, когда получают (я имею в виду, реагируют) их.Главный субъект (прораб) контролирует этот процесс и может получить задание прекратить работу.В этом случае главный актер должен прекратить создавать новые задачи и ждать, когда рабочие завершат все существующие задачи, и только тогда главный актер должен выйти.

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
}

И функция «условие» в «Мастере» будет

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

Я надеюсь, что есть лучшие решения, и вы предложите их.

Ответы [ 4 ]

1 голос
/ 09 февраля 2011

Одна вещь, которой вы можете воспользоваться, это trapExit, case Exit и exit, system.В вашем главном субъекте вы можете установить для 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
    }
    ...
   }}
}

Вы можете сопоставить шаблон по параметру причины.Например, в своем рабочем классе вы можете выйти, используя различные классы дел, указывающие, что должен делать мастер:

// 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.

Надеюсь, это подстегнет интересное решение!

1 голос
/ 09 февраля 2011

Мой подход - делать все расчеты в мастере.

Вы не написали, как бригадир создает задачи, поэтому я предполагаю, что это ответ на сообщение

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
  }
}
1 голос
/ 09 февраля 2011

Если бригадир всегда ожидает ответа от рабочих, решение простое: бригадир поддерживает счетчик, и каждый раз, когда он отправляет сообщение, он увеличивает его, а каждый раз, когда получает одно от рабочего, уменьшает его.Каждый раз, когда счетчик равен нулю, он может остановить себя (при условии, что никто не отправляет сообщения рабочим).

Если бригадир не всегда ожидает ответа от рабочих, вы можете сделать этослучай с отсутствующим сообщением

case object Done { }

и с ответом рабочих, когда они закончат.Затем см. Выше.

Если бригадир не единственный, кто разговаривает с рабочими, или вы хотите, чтобы на заднем плане было меньше разговоров, тогда бригадир и рабочие должны будут договариваться.Бригадир может отправить

case object RequestStop { }

, и рабочие сделают что-то грациозное и ответят Done, когда они закончат.Когда мастер получает столько сообщений Done, сколько отправил RequestStop с, он может выйти.

1 голос
/ 09 февраля 2011

Почему бы не включить ссылку на актера Формана при отправке рабочих мест на рабочих? Затем, когда рабочие закрываются, они могут отправить уведомление мастеру. Каждый раз, когда мастер получает сообщение о закрытии работника, он регистрирует его и проверяет, все ли рабочие завершили. Если так, то он тоже отключается.

...