在本系列文章第一节中,我曾提及,Stateless Future采用线程无关模型。在线程无关模型中,使用Future宏时,程序员不需要关心JVM的线程模型,只需要关注于执行顺序和逻辑流程。不过,Stateless Future中确实有一些约定,决定了线程切换的行为。

Future宏的线程约定

Future宏的功能是把源代码中每一处await调用替换成onComplete调用。每一处await调用以后的所有代码,都会被转换成回调函数,作为参数传给onComplete

例如,给定以下代码:

val future1 = Future {
  beforeAwait()
  val resultOfFuture2 = future2.await
  afterAwait()
  "result of future1"
}

大体相当于

val future1 = new Future.Stateless {
  // 注意:真正的Future宏生成onComplete而非foreach。
  // 为了简化代码,本示例使用foreach代替onComplete,相当于不支持尾调用功能的onComplete
  def foreach(handler: String => Unit)(implicit catcher: Catcher[Unit]) = {
    beforeAwait()
    // 注意:真正的Future宏使用onComplete而非foreach。
    future2.foreach { resultOfFuture2 =>
      afterAwait()
      handler("result of future1")
    }
  }
}

要获取future1的结果时,类似for (resultOfFuture1 <- future1) { println(resultOfFuture1) }的代码会被编译器转换成future1.foreach { resultOfFuture1 -> println(resultOfFuture1) }1。所以,future1内,future2.await上方的代码beforeAwait(),就会在当前线程立即执行。这样就得到了第一个结论:Future宏内的首处await以前的所有代码,都在当前线程立即执行。

afterAwait()位于future2.await,所以会被Future宏捕获到回调函数中,由于这个回调函数作为参数传给future2,所以回调函数什么时候触发,在哪个线程中触发,就由future2决定了。这样就得到了第二个结论:Future宏内每一处xxxFuture.await以后的代码,由xxxFuture决定在哪个线程中执行。

最后,注意Future宏生成了handler("result of future1")用来触发handler以告知future1的结果。这行代码和afterAwait()一样,也位于回调函数中。这样就得到了第三个结论:Future宏内最后一处await决定了Future宏的结果在哪个线程中处理。

总结Future宏对线程模型的约定如下:

  1. Future宏内的首处await以前的所有代码,都在当前线程立即执行。
  2. Future宏内每一处xxxFuture.await以后的代码,由xxxFuture决定在哪个线程中执行。
  3. Future宏内最后一处await决定了Future宏的结果在哪个线程中处理。

  1. for语句到foreach方法调用的转换详见Scala语言规范 6.19 For Comprehensions and For Loops。


杨博

岂凡 游戏架构师