我编写了以下代码,以便在调用Future中的某些异步工作时尝试确定actor对传入消息的行为(实际上,异步工作是为了模拟Slick的异步数据库API ):
val actor = system.actorOf(Props[WTFAsyncActor])
for (i <- 1 until 100) {
actor ! 'wtf + i.toString
Thread.sleep(200l) // yes I know actors should not invoke this method
}
Thread.sleep(10000l) // same as above
演员的代码如下:
class WTFAsyncActor extends Actor with ActorLogging{
import scala.concurrent.ExecutionContext.Implicits.global
override def receive: Receive = {
case i@_ =>
log.info(s"[$i] external block pre future")
Thread.`yield`() // just trying to relinquish control to induce context switching
Future {
log.info(s"[$i] internal block pre sleep")
Thread.sleep(1000l) // yeah bad - trying to emulate something meaningful like slick
log.info(s"[$i] internal block post sleep")
}
log.info(s"[$i] external block post future")
}
}
我得到以下日志(摘自示例运行)
10:17:58.408 [wtf-async-test-akka.actor.default-dispatcher-2] INFO org.wtf.test.WTFAsyncActor - ['wtf1] external block pre future
10:17:58.420 [wtf-async-test-akka.actor.default-dispatcher-2] INFO org.wtf.test.WTFAsyncActor - ['wtf1] external block post future
10:17:58.421 [wtf-async-test-akka.actor.default-dispatcher-2] INFO org.wtf.test.WTFAsyncActor - ['wtf1] internal block pre sleep
10:17:58.599 [wtf-async-test-akka.actor.default-dispatcher-3] INFO org.wtf.test.WTFAsyncActor - ['wtf2] external block pre future
10:17:58.600 [wtf-async-test-akka.actor.default-dispatcher-2] INFO org.wtf.test.WTFAsyncActor - ['wtf2] external block post future
10:17:58.600 [wtf-async-test-akka.actor.default-dispatcher-2] INFO org.wtf.test.WTFAsyncActor - ['wtf2] internal block pre sleep
10:17:58.800 [wtf-async-test-akka.actor.default-dispatcher-2] INFO org.wtf.test.WTFAsyncActor - ['wtf3] external block pre future
10:17:58.801 [wtf-async-test-akka.actor.default-dispatcher-3] INFO org.wtf.test.WTFAsyncActor - ['wtf3] external block post future
10:17:58.801 [wtf-async-test-akka.actor.default-dispatcher-3] INFO org.wtf.test.WTFAsyncActor - ['wtf3] internal block pre sleep
10:17:59.001 [wtf-async-test-akka.actor.default-dispatcher-3] INFO org.wtf.test.WTFAsyncActor - ['wtf4] external block pre future
10:17:59.002 [wtf-async-test-akka.actor.default-dispatcher-3] INFO org.wtf.test.WTFAsyncActor - ['wtf4] external block post future
10:17:59.002 [wtf-async-test-akka.actor.default-dispatcher-3] INFO org.wtf.test.WTFAsyncActor - ['wtf4] internal block pre sleep
10:17:59.202 [wtf-async-test-akka.actor.default-dispatcher-2] INFO org.wtf.test.WTFAsyncActor - ['wtf5] external block pre future
10:17:59.206 [wtf-async-test-akka.actor.default-dispatcher-3] INFO org.wtf.test.WTFAsyncActor - ['wtf5] external block post future
10:17:59.402 [wtf-async-test-akka.actor.default-dispatcher-3] INFO org.wtf.test.WTFAsyncActor - ['wtf6] external block pre future
10:17:59.403 [wtf-async-test-akka.actor.default-dispatcher-2] INFO org.wtf.test.WTFAsyncActor - ['wtf6] external block post future
10:17:59.421 [wtf-async-test-akka.actor.default-dispatcher-3] INFO org.wtf.test.WTFAsyncActor - ['wtf1] internal block post sleep
10:17:59.421 [wtf-async-test-akka.actor.default-dispatcher-3] INFO org.wtf.test.WTFAsyncActor - ['wtf6] internal block pre sleep
10:17:59.607 [wtf-async-test-akka.actor.default-dispatcher-3] INFO org.wtf.test.WTFAsyncActor - ['wtf2] internal block post sleep
10:17:59.607 [wtf-async-test-akka.actor.default-dispatcher-3] INFO org.wtf.test.WTFAsyncActor - ['wtf5] internal block pre sleep
10:17:59.608 [wtf-async-test-akka.actor.default-dispatcher-3] INFO org.wtf.test.WTFAsyncActor - ['wtf7] external block pre future
我认为可以肯定地说,只要有一些线程可用,无论Future块还没有完成执行,新消息都会被视为进入。我对吗?
我设置要揭示的是接收块是否必须等待以便在处理新消息之前完成Future块。 好像不是。 (wtf2在wtf1完成对线程3的计算之前进入调度程序线程3),例如)
这种行为有什么警告吗?
请原谅看似愚蠢的问题。我没有看到akka代码库的内幕(我现在对scala和akka来说太新了),
答案 0 :(得分:5)
在任何Future完成之前,receive
可以处理后续消息。这种行为并没有任何警告,有些事情需要注意。特别是,不要在Future中关闭任何actor的可变状态。 Future可以与actor正在处理的消息同时执行,打破了对actor状态的单线程访问的保证,并导致可能使actor的状态处于无效位置的竞争条件。
如果要在Actor中异步启动某些工作,常见的模式是启动子actor来完成工作。子actor可以使用任何结果将消息发送回其父级,并且您确定子actor不会干扰父actor的状态。
答案 1 :(得分:1)
这种行为没有任何警告。在actor中创建的未来和在其他地方创建的未来在行为上是相同的:它们创建异步计算,该计算将在稍后的某个时间返回成功或失败结果。重要的是,无法保证什么线程将执行未来。很可能,执行你的actor的接收块的相同线程接下来将被分配以处理未来,然后再次被分配给接收块,在这种情况下,似乎所有内容都是同步执行的。但除非你使用单个线程运行,否则这种情况不太可能经常发生。