10.9 控制线程执行

我们已经见识到了,使用receive时,每个actor是怎样运行在自己的线程里,react又如何让actor共享来自线程池的线程。不过,有时候我们会想要更强的控制力。比如,结束一个长期运行的任务之后,需要更新UI,这时需要在一个单独的线程里运行任务,然后,在主线程里更新UI。(因为UI组件时常不是线程安全的。)通过使用SingleThreadedScheduler,可以让Scala在主线程里运行actor。我们用个例子看看如何做到这点:

ConcurrentProgramming/InMainThread.scala

  1. import scala.actors._
  2. import Actor._
  3. if (args.length > 0 && args(0) == "Single"){
  4. println("Command-line argument Single found")
  5. Scheduler.impl = new SingleThreadedScheduler
  6. }
  7. println("Main running in " + Thread.currentThread)
  8. actor { println("Actor1 running in " + Thread.currentThread) }
  9. actor { println("Actor2 running in " + Thread.currentThread) }
  10. receiveWithin(3000) { case _ => }

上面的代码里,创建了两个actor。如果不传任何命令行参数,两个actor的代码和主脚本的代码会运行在各自的线程里,输出如下:

  1. Main running in Thread[main,5,main]
  2. Actor2 running in Thread[Thread-5,5,main]
  3. Actor1 running in Thread[Thread-3,5,main]

另一方面,如果像scala InMainThread.scala Single这样运行之前的代码,会得到不同的结果:

  1. Command-line argument Single found
  2. Main running in Thread[main,5,main]
  3. Actor1 running in Thread[main,5,main]
  4. Actor2 running in Thread[main,5,main]

无论actor何时启动,Scala都会让单例对象Scheduler去运行它。通过设置Schedulerimpl,就可以控制整个应用的actor调度策略。

上面的方式影响深远;它让我们可以控制所有actor的调度。不过,也许我们想要让一些线程运行在主线程里,而其他actor运行在各自线程里。通过继承Actor trait,改写scheduler()方法,就可以做到这一点。默认情况下,这个方法为要调度的actor返回单例对象Scheduler。改写这个方法就可以控制调度单独的actor的方式,如下所示:

ConcurrentProgramming/InMainThreadSelective.scala

  1. import scala.actors._
  2. import Actor._
  3. trait SingleThreadedActor extends Actor {
  4. override protected def scheduler() = new SingleThreadedScheduler
  5. }
  6. class MyActor1 extends Actor {
  7. def act() = println("Actor1 running in " + Thread.currentThread)
  8. }
  9. class MyActor2 extends SingleThreadedActor {
  10. def act() = println("Actor2 running in " + Thread.currentThread)
  11. }
  12. println("Main running in " + Thread.currentThread)
  13. new MyActor1().start()
  14. new MyActor2().start()
  15. actor { println("Actor 3 running in " + Thread.currentThread) }
  16. receiveWithin(5000) { case _ => }

上面的代码创建了三个actor,其中,两个继承自Actor trait,一个使用了更为常规的actor()方法。通过改写protected方法scheduler(),就可以控制MyActor2的线程。运行上面的代码时,使用actor()MyActor1创建的actor运行于自己的线程。而使用MyActor2创建的actor则运行于主线程,如下所示:

  1. Main running in Thread[main,5,main]
  2. Actor1 running in Thread[Thread-2,5,main]
  3. Actor2 running in Thread[main,5,main]
  4. Actor 3 running in Thread[Thread-4,5,main]