Могут ли актеры Scala обрабатывать несколько сообщений одновременно?
-
06-07-2019 - |
Вопрос
В ответе на мой недавний вопрос указано что актер обрабатывал свои сообщения по одному . Это правда? Я не вижу ничего такого, что явно говорит об этом (в Программирование в Scala ), которое содержит следующий фрагмент (стр. 593)
Если [метод
реагировать
] находит сообщение, которое можно обработать, [оно] запланирует обработку этого сообщения для последующего выполнения и выдаст исключение р>
(выделю мое) Два связанных (и взаимоисключающих) вопроса:
<Ол> receive
?) edit: выполнение небольшого тестирования, кажется, подтверждает, что я ошибаюсь, и что актеры действительно последовательны. Так что это вопрос № 2, на который мне нужно ответить
Решение
Актеры обрабатывают одно сообщение за раз. Классический шаблон для обработки нескольких сообщений - наличие одного фронта координатора для группы потребителей. Если вы используете реагировать, то пул потребителей может быть большим, но все равно будет использовать только небольшое количество потоков JVM. Вот пример, где я создаю пул из 10 потребителей и одного координатора для них.
import scala.actors.Actor
import scala.actors.Actor._
case class Request(sender : Actor, payload : String)
case class Ready(sender : Actor)
case class Result(result : String)
case object Stop
def consumer(n : Int) = actor {
loop {
react {
case Ready(sender) =>
sender ! Ready(self)
case Request(sender, payload) =>
println("request to consumer " + n + " with " + payload)
// some silly computation so the process takes awhile
val result = ((payload + payload + payload) map {case '0' => 'X'; case '1' => "-"; case c => c}).mkString
sender ! Result(result)
println("consumer " + n + " is done processing " + result )
case Stop => exit
}
}
}
// a pool of 10 consumers
val consumers = for (n <- 0 to 10) yield consumer(n)
val coordinator = actor {
loop {
react {
case msg @ Request(sender, payload) =>
consumers foreach {_ ! Ready(self)}
react {
// send the request to the first available consumer
case Ready(consumer) => consumer ! msg
}
case Stop =>
consumers foreach {_ ! Stop}
exit
}
}
}
// a little test loop - note that it's not doing anything with the results or telling the coordinator to stop
for (i <- 0 to 1000) coordinator ! Request(self, i.toString)
Этот код проверяет, какой потребитель доступен, и отправляет запрос этому потребителю. Альтернативы - просто случайным образом назначать потребителям или использовать планировщик циклического перебора. Р>
В зависимости от того, что вы делаете, вам может быть лучше подано с Scala's Futures. Например, если вам не нужны актеры, тогда все вышеперечисленные механизмы можно записать как
import scala.actors.Futures._
def transform(payload : String) = {
val result = ((payload + payload + payload) map {case '0' => 'X'; case '1' => "-"; case c => c}).mkString
println("transformed " + payload + " to " + result )
result
}
val results = for (i <- 0 to 1000) yield future(transform(i.toString))
Другие советы
Я думаю, что ответ таков: Actor
не может обрабатывать сообщения асинхронно. Если у вас есть Actor
, который должен прослушивать сообщения, в которых эти сообщения могут обрабатываться асинхронно , то это можно записать так:
val actor_ = actor {
loop {
react {
case msg =>
//create a new actor to execute the work. The framework can then
//manage the resources effectively
actor {
//do work here
}
}
}
}
Если вы хотите сделать несколько вещей, вы должны использовать несколько актеров. Основная причина использования действующих лиц заключается в том, чтобы разделить работу между несколькими независимыми процессами.