何时为Scala actor创建的线程会做出反应?

时间:2009-10-06 17:01:42

标签: scala

在阅读了Scala中的演员使用react之后,我认为react会共享同一个帖子,因为没有多个react未决。情况似乎并非如此。

import scala.actors.Actor
import scala.actors.Actor._

class SleepyReactor extends Actor {
    def act() {
        loop {
            react {
                case x => {
                    println("reacting to %s on thread %s".format(x, Thread.currentThread.getName))
                    Thread.sleep(1000)
                    println("done with " + x)
                }
            }
        }
    }
}
val sleepyOne = new SleepyReactor
sleepyOne.start
sleepyOne ! "first" // runs on thread-5

// wait until completion

sleepyOne ! "second" // runs on thread-3

有人可以解释为什么这些react在不同的线程上运行,以及何时为react的角色创建新线程?

我在某处读到react是基于事件的,我认为这意味着“反应角色”共享一个线程,如果一个人“反应”,其他“反应角色”将排队等到第一个完成。我现在认为我错了。这是如何工作的,它与收到的有何不同?

4 个答案:

答案 0 :(得分:9)

对于纯粹的基于事件的actor,它的反应代码与消息发送代码在同一个线程上运行。

但是在Scala中,因为当一个actor在其反应代码中调用阻塞操作并统一基于事件和基于线程的actor(能够组合它们)时,阻止一个线程是不可取的,所以这两种类型的actor都使用相同的线程池但基于线程的actor获取自己的线程,而基于事件的actor根据任务队列共享线程。有关详细信息,请参阅Philipp Haller和Martin Odersky的Actors that Unify Threads and Events

答案 1 :(得分:5)

不要假设每个Actor都有一个单独的线程。 Scala机制创建了一个工作线程池,只有在“阻塞”Actors的大小大于池大小时才会增长该池。当你的演员调用receive时,它会处于阻塞状态,直到收到消息为止。

答案 2 :(得分:4)

要查看前面答案中描述的效果,您需要生成两个以上的线程。此示例程序生成100个带有receive的线程和100个带有react的线程。

当你运行它时,你可以看到每个接收actor占用一个线程,而反应者共享少量线程。 (在输出排序时最容易看到。)

import scala.actors._
import scala.actors.Actor._

class ReactActor extends Actor {
  def act {
    loop {
      react {
        case 'Hello => println("React: " + Thread.currentThread)
      }
    }
  }
}

class ReceiveActor extends Actor {
  def act {
    while (true) {
      receive {
        case 'Hello => println("Receive: " + Thread.currentThread)
      }
    }
  }
}

object Main {
  def main(args: Array[String]) {
    val count = 100
    val as = new Array[Actor](2 * count)
    for (i <- 0 until count) {
      as(i) = new ReactActor 
      as(count + i) = new ReceiveActor
    } 
    for (a <- as) a.start()
    actor {
      println(Thread.currentThread)
      for (a <- as) a ! 'Hello
    }
  }
}

典型程序中的排序输出:

Thread[Thread-102,5,main]
React: Thread[Thread-102,5,main]
React: Thread[Thread-102,5,main]
React: Thread[Thread-102,5,main]
...
React: Thread[Thread-102,5,main]
React: Thread[Thread-103,5,main]
React: Thread[Thread-103,5,main]
...
React: Thread[Thread-103,5,main]
React: Thread[Thread-104,5,main]
React: Thread[Thread-104,5,main]
React: Thread[Thread-104,5,main]
...
React: Thread[Thread-104,5,main]
React: Thread[Thread-105,5,main]
React: Thread[Thread-105,5,main]
React: Thread[Thread-105,5,main]
...
React: Thread[Thread-105,5,main]
Receive: Thread[Thread-1,5,main]
Receive: Thread[Thread-10,5,main]
Receive: Thread[Thread-100,5,main]
Receive: Thread[Thread-101,5,main]
Receive: Thread[Thread-11,5,main]
Receive: Thread[Thread-12,5,main]
Receive: Thread[Thread-13,5,main]
Receive: Thread[Thread-14,5,main]
Receive: Thread[Thread-15,5,main]
Receive: Thread[Thread-16,5,main]
Receive: Thread[Thread-17,5,main]
Receive: Thread[Thread-18,5,main]
Receive: Thread[Thread-19,5,main]
Receive: Thread[Thread-2,5,main]
Receive: Thread[Thread-20,5,main]
Receive: Thread[Thread-21,5,main]
...

答案 3 :(得分:3)

调度程序库使用线程池来控制actor的执行。我不知道它使用的逻辑的具体细节,但对我来说,期望它是自然的:

  • 使用池中的多个线程进行初始化,因为多线程应用程序很可能使用多个thead。

  • 以类似队列的方式选择与等待的actor一起使用的线程 - 线程被释放到队列的末尾,并从队列的开头获取。

此外,我假设一些线程用于处理调度本身以及消息传递。