统一了线程和事件的Actors(Actors That Unify Threads and Events)(第三节)

统一了线程和事件的Actors(Actors That Unify Threads and Events)(第三节)
guibin.beijing@gmail.com

本文翻译自[url]http://lamp.epfl.ch/~phaller/doc/haller07actorsunify.pdf[/url],原作者 Philipp Haller 和 Martin Odersky.

第三节. 实例
在这一节中我们用一个大的例子来讨论actor模型给我们带来的好处。在这个过程中,我们仔细分析三种不同的实现:一个事件驱动的版本,一个基于线程的版本,一个使用Scala的actor的版本。

[quote="Fig. 2. Producers that generate all values in a tree in in-order"] class InOrder(n: IntTree)
extends Producer[int] {
def produceValues = traverse(n)
def traverse(n: IntTree) {
if (n != null) {
traverse(n.left)
produce(n.elem)
traverse(n.right)
}
}
}

class InOrder(n: IntTree)
extends Producer[int] {
def produceValues = traverse(n, {})
def traverse(n: Tree, c: => unit) {
if (n != null) {
traverse(n.left, produce(n.elem,
traverse(n.right, c)))
} else c
}
} [/quote]

我们将要写一个producer的抽象,这个producer提供标准的iterator接口以获取一系列生成出来的值。Pruducer需要实现一个抽象的[b]produceValues[/b]方法,这个[b]produceValues[/b]方法调用[b]produce[/b]以生成单独的值。这两个方法都继承自类[b]Producer[/b]。作为一个列子,figure 2中的第一个例子展示了producer的定义,这个producer通过中序遍历一个预先存了值的二叉树生成需要的值。

[quote="Fig. 3. Event-driven and threaded producers"]abstract class CPSProducer[T] {
var next: Option[T] = None
var savedCont: () => unit =
() => produceValues
def produce(x: T,
cont: => unit) {
next = Some(x)
savedCont = () => {
next = None; cont
}
}
...
}


abstract class ThreadedProducer[T] {
val produced = new Queue[Option[T]]
def next: Option[T] = synchronized {
while (produced.isEmpty) {wait()}
produced.dequeue
}
new Thread(new Runnable() {
def run() {
produceValues
produced += None
}
}).start()

def produce(x: T) = synchronized {
produced += Some(x)
if (produced.length == 1) notify()
}
...
}
[/quote]

在纯的事件驱动的版本中,基本上有两种方法遍历,也就是通过连续传值方式遍历(continuation-passing style -CPS),或者是显示的FSM编程。在figure 3的第一段程序展示了用事件驱动的方法实现了producers,在这种实现方法中运用了CPS方式的遍历。思路是[b]produce[/b]方法是一个被传递中的持续闭包,当下一个值要被产生时,这个方法将会被调用。比如之前提到的使用中序遍历的二叉树实现的方法,即Figure 2的第二段程序,通过使用producer的实例变量来改变生成的值。

figure 3的第二段程序展示了一个使用线程实现的producer的抽象。在基于线程实现的版本中,迭代的状态在运行[b]produceValues[/b]方法的堆栈中隐式的维护着。生成的值被放进一个能够同其他迭代器通信的队列中。在向一个空队列请求值时,此时会阻塞运行迭代器的线程(guibin注,在此例中迭代器是指运行next方法)。同基于事件驱动的实现版本相比,基于线程的版本简化了对迭代策略的指定(guibin注:原文 simplifies the specification of iteration strategies)。为了定义一个具体的迭代器,具体的迭代器中必须实现能够按照指定的顺序遍历二叉树的[b]produceValues[/b]方法。

[quote="Fig. 4. Implementation of the producer and coordinator actors"]abstract class ActorProducer[T] {
def produce(x: T) {
coordinator ! Some(x)
}
private val producer = actor {
produceValues
coordinator ! None
}
...
}


private val coordinator = actor {
loop { receive {
case ’next => receive {
case x: Option[_] => reply(x)
}
}}
}
[/quote]

Figure 4展示了使用两个actor实现的producer,一个[b]producer[/b] actor,一个[b]coordinator[/b] actor。producer运行[b]produceValues[/b]方法进而能够给coordinator产生一系列包裹在Some中的值,这些序列以None结尾。coordinator用来同步来自客户端的请求和来自producer的值。在基于线程的版本中,[b]produce[/b]方法没有接受一个持续性的参数。

在基于actor版本的实现中,改进了在基于事件的版本中不向CPS中指定的遍历(guibin注:原文The actor-based version improves over the event-driven version by not requiring to specify the traversal in CPS.)。此外基于actor版本的实现支持迭代器的并发,由于基于邮箱的通信是非竞争的。基于同样的原因,在基于线程的实现版本中,没有必要使用显示的阻塞队列,因为这个功能已经被actor的邮箱实现了。我们相信使用阻塞队列通信是非常的常见,以至于很有必要把这些队列都做成支持并发的actor的邮箱的形式。


Guibin
2011-03-20
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值