Scala, проверьте, вышел ли Актер
в Scala 2.8, когда я начинаю актеров, я могу общаться через передачу сообщений. Это, в свою очередь, означает, что я могу отправить окончательное сообщение Exit() или все, что я решаю, подходит для моего протокола.
Но как я могу проверить, вышел ли актер? Я легко могу представить себе, что у меня есть задача, когда мастер-актер запускает некоторых действующих актеров, а затем просто ждет ответов, каждый раз проверяя, был ли это окончательный ответ (т.е. Какие-либо Актеры все еще работают или все они выходили?).
Конечно, я могу позволить им отправить сообщение "Я сделан", а затем посчитать их, но это как-то неудовлетворительно.
Какова наилучшая практика при тестировании для завершения работника-актера?
РЕДАКТИРОВАТЬ # 1
Эй, ребята, я смотрю в Futures, но у меня проблемы. Может кто-то объяснить, почему этот код не работает:
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())
}
Это работает, если я определяю функцию fib внутри тела будущего. Это должно быть областью видимости, но я не получаю никаких ошибок с вышеупомянутым, это просто зависает. Кто-нибудь?
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())
}
}
Я поклонник сообщений "Я сделан" лично; это хороший способ управлять распределением работы, и в качестве бонуса вы уже знаете, когда все дети закончили то, что они делают.
Но если вы действительно хотите развернуть какую-то работу один раз и дождаться, когда все будет готово, проверьте 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
Некоторое время назад я писал сообщение об увязке участников в Scala. Связывание актера - это идиоматический [и самый простой] способ контролировать актеров в Erlang, Scala Актерах и других актерских библиотеках. По 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 = scala.actors.Actor$$anon$1@1feea62
scala>
scala> val actor2 = actor {
| link(actor1)
| self.trapExit = true
| loop {
| react {
| case msg => println(msg)
| }
| }
| }
actor2: scala.actors.Actor = scala.actors.Actor$$anon$1@1e1c66a
scala> actor1.start
res12: scala.actors.Actor = scala.actors.Actor$$anon$1@1feea62
scala> actor2.start
res13: scala.actors.Actor = scala.actors.Actor$$anon$1@1e1c66a
scala> actor1 ! Stop
Actor 1: stop
scala> Exit(scala.actors.Actor$$anon$1@1feea62,'normal) // Actor 2 received message, when Actor1 died
Более сложным и гибким способом является использование супервизоров (поведение супервизора в Erlang, исполнители актеров в Akka Actors library, и т.д). Супервизор (сам по себе актер) контролирует ряд других участников и перезапускает их в отношении определенной стратегии (перезапустите всех актеров, если умрет, перезапустите только одного актера, когда он умрет).
Хорошо, все, я придумал решение, используя функцию getState класса actor. В решении я использовал идею из этой темы: Лучший способ заглянуть в почтовый ящик Scala Актерa > в которой используется реакция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) и получаю?