2010-11-28 7 views
5

в Scala 2.8, когда я начинаю актеров, я могу общаться через передачу сообщений. Это, в свою очередь, означает, что я могу отправить окончательное сообщение Exit() или все, что я решаю, подходит для моего протокола.Скала, проверьте, вышел ли Актер

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

Конечно, я могу позволить всем им отправить сообщение «Я сделан», а затем посчитать их, но это как-то неудовлетворительно.

Какова наилучшая практика при тестировании для завершения работника-актера?

EDIT # 1

Эй, ребята, я смотрю на фьючерсы, но возникли проблемы. Может кто-нибудь объяснить, почему этот код не работает:

package test 
import scala.actors.Futures._ 

object FibFut extends Application{ 

    def fib(i:Int):Int = 
     if(i<2) 
      1 
     else 
      fib(i-1)+fib(i-2) 

    val f = future{ fib(3) } 

    println(f())  

} 

Это работает, если я определить функцию выдумка внутри будущего тела. Это должно быть областью видимости, но я не получаю никаких ошибок с вышеупомянутым, это просто зависает. Кто угодно?

EDIT # 2

кажется, что расширение приложения не был хороший путь. Определение основного метода заставило все работать. Ниже код, что я искал, так что фьючерсы получить палец вверх :)

package test 

import scala.actors.Futures._ 

object FibFut { 

    def fib(i: Int): Int = if (i < 2) 1 else fib(i - 1) + fib(i - 2) 

    def main(args: Array[String]) { 

    val fibs = for (i <- 0 to 50) yield future { fib(i) } 

    for (future <- fibs) println(future()) 

    } 

} 
+3

Эту тему мне интересна: http://stackoverflow.com/questions/2721337/best-method-to-peek-into-a-scala-actors-mailbox, а также использование этого трюка в списке участников: computers.map (_. getState == Actor.State.Terminated) .reduceRight (_ && _), чтобы проверить, завершены ли все участники внутри списка компьютеров. Если это так, главный поток может перейти в состояние «закончить чтение почтового ящика и выйти» с использованием if reactwithin. Я отправлю решение, если я это сделаю :-) – Felix 2010-11-29 13:53:14

ответ

3

Я лично являюсь поклонником сообщений «Я сделан»; это хороший способ управлять распределением работы, и в качестве бонуса вы уже знаете, когда все дети закончили то, что они делают.

Но если вы действительно хотите развести какую-либо работу один раз и дождитесь, пока все будет готово, ознакомьтесь с scala.actors.Futures.Вы можете попросить его сделать некоторые вычисления:

val futureA = Futures.future { 
    val a = veryExpensiveOperation 
    (a,"I'm from the future!") 
} 

, а затем вы можете ждать, чтобы все закончить, если вы сделали несколько запросов:

Futures.awaitAll(600*1000, futureA, futureB, futureC, futureD) 
// Returns once all of A-D have been computed 
val actualA = futureA() // Now we get the value 
2

Некоторое время назад я написал a post о связывании актеров в Scala. Связывание актера - это идиоматический [и самый простой] способ контролировать актеров в Erlang, Scala Actors и других актерских библиотеках. По defalt, когда вы соединяете 2 актеров, и один из них умирают, другие сразу умирает тоже (если актер ловушка/не обрабатывает сигнал выхода):

scala> case object Stop 
defined module Stop 

scala> 

scala> val actor1 = actor { 
    | loop { 
    |  react { 
    |   case Stop => 
    |    println("Actor 1: stop") 
    |    exit() 
    |   case msg => println(msg) 
    |    } 
    |   } 
    | } 
actor1: scala.actors.Actor = [email protected] 

scala> 

scala> val actor2 = actor { 
    | link(actor1) 
    | self.trapExit = true 
    | loop { 
    |  react { 
    |   case msg => println(msg) 
    |    } 
    |   } 
    | } 
actor2: scala.actors.Actor = [email protected] 

scala> actor1.start 
res12: scala.actors.Actor = [email protected] 

scala> actor2.start 
res13: scala.actors.Actor = [email protected] 

scala> actor1 ! Stop 
Actor 1: stop 

scala> Exit([email protected],'normal) // Actor 2 received message, when Actor1 died 

Более сложный и гибкий способ используют контролер (супервизор поведение Erlang, actor supervisors в Akka Actors library и т. Д.). Наблюдатель (сам по себе актер) контролирует ряд других участников и перезапускает их в соответствии с определенной стратегией (перезапустите всех участников, если умрет, перезапустите только одного актера, когда он умрет).

+0

связь далеко не идеальна. Во-первых, я считаю, что если вы попытаетесь связаться с актером, который уже вышел, вы не получите уведомление. Во-вторых, ссылка на актера, который падает до того, как он входит в какой-либо цикл реакции/приема, также пропускает любые обратные вызовы. – 2010-11-28 17:45:38

+0

Связывание является идиоматическим. В обоих случаях, о которых вы упоминали, перед связыванием имеет смысл называть `getState`, и убедитесь, что состояние актера отличается от« завершено ». – 2010-11-28 17:55:29

0

Хорошо все, я пришел с решением используя функцию getState класса actor. В решении я использовал идею из этой темы: Best method to peek into a Scala Actor's Mailbox , в которой используется реакцияWithin (0). У меня возникли проблемы при использовании реакции и цикла, где программа просто блокировала большие вычисления. Это было решено путем замены цикла while (true) и reactWithin (int) с помощью receiveWithin (int).

Мое решение выглядит следующим образом (остерегайтесь, Bigass кодовую комок):

package test 

import scala.actors._ 
import scala.actors.Actor.State._ 

case class Computation(index: Int, a:() ⇒ Int) 
case class Result(i: String) 
object Main { 
    def main(args: Array[String]) { 
    val m = new Master 
    m.start 
    } 
} 

class Master extends Actor { 

    val N = 40 
    var numberOfAnswers = 0 

    def fib(x: Int): Int = 
    if (x < 2) 
     1 
    else 
     fib(x - 1) + fib(x - 2) 

    val computers = for (i ← 0 to N) yield new Computer 

    def act { 

    for (i ← 0 until computers.size) { 
     computers(i).start 
     computers(i) ! Computation(i,() => fib(i)) 
    } 

    println("done Initializing actors") 
    while (true) { 
     receiveWithin(1000) { 

     case Result(i) => 
      val numberDone = computers.map(_.getState == Terminated).filter(_ == true).length 
      println(i) 
      numberOfAnswers += 1 

     case TIMEOUT => 
      val allDone = computers.map(_.getState == Terminated).reduceRight(_ && _) 
      println("All workers done?:" + allDone) 
      println("# of answers:" + numberOfAnswers) 
      if (allDone) 
      exit() 
     } 
    } 

    } 

} 

class Computer extends Actor { 

    def act { 
    loop { 
     react { 
     case Computation(i, f) ⇒ 
      sender ! Result("#" + i + " Res:" + f()) 
      exit() 
     } 
    } 
    } 

} 

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

computers.map(_.getState == Terminated).reduceRight(_ && _) 

где компьютеры относятся к типу IndexedSeq [Computer]. Хитрость заключается в том, что, используя сообщение TIMEOUT, я могу периодически проверять, выполняется ли вся работа и действовать соответственно (в этом случае выйдите, когда больше нет активных работников). Я использую тот факт, что каждый работник отправляет результаты до их выхода. Таким образом, я знаю, что всегда буду получать результаты и обрабатывать их до того, как они будут показаны как завершенные.

Может кто-нибудь прокомментировать факт, что программа «блокирует» (прекращает принимать сообщения), когда я использую реакцию и цикл вместо while (true) и получать?

 Смежные вопросы

  • Нет связанных вопросов^_^