Взаимодействие с актером Аккой за пределами актеров

Я хочу взаимодействовать с актерами Akka из моей собственной нити. В настоящее время мне нравится:

val res = Await.result(aref ? GroupReceive(fromRank), timeout.duration).asInstanceOf[T]

Но я не уверен, как это действительно взаимодействует с моим потоком? Я хочу, чтобы приём был асинхронным, т.е. Я хочу повесить поток при получении, чтобы разрешить выполнение какой-либо другой работы. Недавно я прочитал о системе входящих сообщений Akka. inbox akka api

Думаю, я помню, что Await каждый раз создает нового актера. Каковы различия между await + ask и inbox, и может ли кто-нибудь дать мне пример того, как создать почтовый ящик и использовать его для общения с актерами "снаружи"?

ИЗМЕНИТЬ Чтобы уточнить, я не хочу, чтобы тот же самый поток продолжал работать, я хочу, чтобы он прекратил запугивание ядра процессора и оставил другие потоки работать до тех пор, пока не получил что-то, а затем снова проснется.

Ответ 1

Как написано в документации Akka Future, использование Await блокирует текущий поток до ожидания результата.

Пример

import scala.concurrent.Await
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.duration._

implicit val timeout = Timeout(5 seconds)
val future = actor ? msg // enabled by the "ask" import
val result = Await.result(future, timeout.duration).asInstanceOf[String]

Это заставит текущий поток блокировать и ждать, пока актер "завершит" будущее с ответом.

Использование с участниками

Ответ 2

Await.receive является частью API Scala concurrency и не имеет ничего общего с актерами. Его цель - блокировать текущий поток до тех пор, пока предоставленное будущее не завершится, или ограничение тайм-аута не начнется, а все закончится исключением тайм-аута.

Оператор ask ? действительно создаст временного актера с единственной целью - ждать ответа от актера, на который указывает переменная aref, и завершить свое будущее, когда вы вызвали оператора запроса с полученным ответить.

Итак, ваш код по существу блокирует весь поток. Как было указано, если вы хотите освободить текущий поток и продолжить выполнение какой-либо другой работы, вы можете приложить обратный вызов в будущее.

implicit val ctx: ExecutionContext = //provide execution context here
implicit val timeout: Timeout = // provide timeout here
aref ? GroupReceive(fromRank)) onSuccess { res =>
   //do something with res here, asynchronously
}
// some other code which runs without being blocked...

Вышеупомянутый код можно переписать с помощью DSL актера, упомянутого выше:

import akka.actor.ActorDSL._
implicit val actorSystem: ActorSystem = // provide an actor system here or any actor ref factory

actor(new Act {
  aref ! GroupReceive(fromRank)
  context.setReceiveTimeout(timeout) //optional
  become {
    case ReceiveTimeout => {
      //handle the timeout
      context.stop(self)
    }
    case res => {
      //do your thing with res, asynchronously
      context.stop(self)
    }
  }
}

//some other code which won't wait for the above operations

В последней версии также создается новый временный актер, который отправляет сообщение GroupReceive, а затем ждет ответа, после которого он убивает себя.

Суть в том, что для того, чтобы получить сообщение от актера, вы должны быть самим актером. Актеры не могут просто отправить сообщение на что-то другое, кроме ActorRef.

Итак, либо вы используете шаблон запроса, который создает временный актер за кулисами, и управляет этим временным жизненным циклом актера, предоставляя вам простое простое будущее для работы, или вы можете создать временного актера самостоятельно, но тогда вы должны управлять своим жизненным циклом (т.е. не забудьте убить его, как только он выполнил свою работу)

Выберите наиболее подходящий вариант.

Ответ 3

Если вы не хотите блокировать вызывающую сторону, тогда не используйте Await, используйте неблокирующие обратные вызовы, а не onSuccess, onFailure и onComplete. Когда вы это делаете, будущая задача помещается в любой объект ExecutionContext в момент запроса (?). Когда ответ получен, этот обратный вызов вызывается асинхронно через ExecutionContext. Таким образом, вы избегаете блокировки в потоке, который делает запрос к актеру, а затем обратный вызов обрабатывается в пуле потоков, привязанном к ExecutionContext.

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