10.5 actor方法

在上面的例子里,我们控制了actor何时启动。如果对显式启动actor并不真的那么关注,那么可以使用actor()方法。在actor间传递数据,可以用!()receive()方法。下面从一个使用actor()方法的例子开始,然后重构,使其并发。

这个方法(isPrime())告诉我们给定的数是不是素数。为了达到说明的目的,我在方法里加了一些打印语句:

ConcurrentProgramming/PrimeTeller.scala

  1. import scala.actors._
  2. import Actor._
  3. def isPrime(number: Int) = {
  4. println("Going to find if " + number + " is prime")
  5. var result = true
  6. if (number == 2 || number == 3) result = true
  7. for (i <- 2 to Math.sqrt(number.toDouble).floor.toInt; if result) {
  8. if (number % i == 0) result = false
  9. }
  10. println("done finding if " + number + " is prime")
  11. result
  12. }

调用上面这段代码的话,接收到应答之前,就会阻塞在那里。如下所示,这里把调用这个方法的职责委托给一个actor。这个actor会确定一个数是否是素数,然后,用一个异步响应发回给调用者。

ConcurrentProgramming/PrimeTeller.scala

  1. Line 1 val primeTeller = actor {
  2. 2 var continue = true
  3. 3
  4. 4 while (continue) {
  5. 5 receive {
  6. 6 case (caller : Actor, number: Int) => caller ! (number,
  7. isPrime(number))
  8. 7 case "quit" => continue = false
  9. 8 }
  10. 9 }
  11. 10 }

primeTeller是一个引用,它指向了用actor()方法创建的一个匿名actor。它会不断循环,直到接收到“quit”消息。除了退出消息,它还能接收一个包含callernumber的元组。收到这个消息时,它会判断给定的数是否是素数,然后,给caller发回一个消息。

下面让这个actor判断任意三个数(2, 131, 132)是否是素数:

ConcurrentProgramming/PrimeTeller.scala

  1. primeTeller ! (self, 2)
  2. primeTeller ! (self, 131)
  3. primeTeller ! (self, 132)
  4. for (i <-1 to 3){
  5. receive {
  6. case (number, result) => println(number + " is prime? " + result)
  7. }
  8. }
  9. primeTeller ! "quit"

上面的代码处理了接收到的每个数字;从下面的输出上,可以看到这一点。在actor忙于判断一个数是否是素数时,如果又接收到多个请求,它们就会进入队列。因此,即便是将执行委托给了actor,它依然是顺序的。

  1. Going to find if 2 is prime
  2. done finding if 2 is prime
  3. 2 is prime? true
  4. Going to find if 131 is prime
  5. done finding if 131 is prime
  6. Going to find if 132 is prime
  7. 131 is prime? true
  8. done finding if 132 is prime
  9. 132 is prime? false

别害怕,让这个例子并行相当容易,这样,它就可以同时处理多个请求了。在primeTeller actor的第6行,不要去调用isPrime(),而是把这个职责委托给另一个actor,让它给调用者回复应答:

  1. //case (caller : Actor, number: Int) => caller ! (number, isPrime(number))
  2. case (caller : Actor, number: Int) => actor { caller ! (number,
  3. isPrime(number)) }

再次运行上面的代码,我们会看到,多个请求并发地执行了,如下所示:

  1. Going to find if 131 is prime
  2. Going to find if 2 is prime
  3. Going to find if 132 is prime
  4. done finding if 2 is prime
  5. done finding if 131 is prime
  6. 2 is prime? true
  7. 131 is prime? true
  8. done finding if 132 is prime
  9. 132 is prime? false

以线程安全的方式编写并发代码毫不费力。记住,这里成功的关键在于不变对象。绝对不要在线程间共享公共状态——我是指actor。

上面的输出还可以从另外一个角度观察——与actor交互的顺序是没有任何保证的。actor接收到消息就可以进行处理,只要准备好就可以应答。actor对消息的接收和处理没有预先强加的顺序。