Actor、Coroutine和Continuation的概念澄清
那么,什么是Continuation?这个要从表达式的求值说起。一个表达式的求值可以分为两个阶段:“What to evaluate?”和“What to do with the value”,“What to do with the value”就是计算的Continuation。以下面这段代码为例:
return x+1
else
return x
end
考察其中的表达式x<3,这个表达式就是“what to evaluate?”,代表你将计算的东西,然后根据x<3的结果决定是执行x+1还是直接返回x,这个根据x<3的值来决定下一步的过程就是这个表达式的Continuation,也就是"what to do with the value"。怎么得到某个表达式的Continuation呢?在支持Continuation的语言里提供了call-with-current-continuation的函数,通常简称为call/cc,使用这个函数你就可以在任何代码中得到Continuation。进一步,continuation有什么作用呢?它可以做的事情不少,如nonlocal exits、回溯、多任务的实现等等。例如在scheme中没有break语句,就可以用call/cc实现一些此类高级的控制结构:
(for-each (lambda (x) (if (< x 0) (break x)))
'(99 88 -77 66 55))
#t))
上面这段代码查找列表(99 88 -77 66 55)中的负数,当查找到的时候马上从迭代中退出并返回该值,其中的break就是一个continuation。刚才还提到continuation可以实现回溯,那么就可以实现一个穷举的机器出来用来搜索解空间,也就是类似Prolog中的回溯机制,在SICP这本书里就介绍了如何用call/cc实现一个简单的逻辑语言系统。更著名的就是神奇的amb操作符,有兴趣可以看看这里。
接下来我们来看看如何continuation实现多任务,在Continuation的维基百科里给了一段代码来展示如何用scheme来实现coroutine,我稍微修改了下并添加了注释:
(define call/cc call-with-current-continuation)
(define *queue* '())
(define (empty-queue?)
(null? *queue*))
(define (enqueue x)
(set! *queue* (append *queue* (list x))))
(define (dequeue)
(let ((x (car *queue*)))
(set! *queue* (cdr *queue*))
x))
;;启动协程
(define (resume proc)
(call/cc
(lambda (k)
;;保存当前continuation,执行proc
(enqueue k)
(proc))))
;;让出执行权
(define (yield)
(call/cc
(lambda (k)
;;保存当前continuation,弹出上一次执行的cont并执行
(enqueue k)
((dequeue)))))
;;停止当前协程或者当没有一个协程时停止整个程序,最简单的调度程序
(define (thread-exit)
(if (empty-queue?)
(exit)
((dequeue))))
这其实就是一个coroutine的简单实现,context的保存、任务的调度、resume/yield原语……样样俱全。使用起来类似这样,下面这段程序轮流打印字符串:
(lambda()
(let loop()
(display str)
(newline)
(yield)
(loop))))
;;;创建两个协程并启动调度器
(resume (display-str "This is AAA"))
(resume (display-str "Hello from BBB"))
(thread-exit)
任务非常简单,打印下传入的字符串并换行,然后让出执行权给另一个任务执行,因此输出:
Hello from BBB
This is AAA
Hello from BBB
This is AAA
Hello from BBB
This is AAA
Hello from BBB
……
谈了这么多continuation的应用,事实上我想说明的是continuation可以用来实现协程,Ruby 1.9中call/cc和Fiber的实现(在cont.c)大体是一样的同样说明了这一点。
接下来我们讨论下Actor和Coroutine的关系,上面提到Actor是一种并发模型,我更愿意称之为一种编程风格,Actor跟message passing、Duck Typing是一脉相承的。Actor风格是可以这么描述:将物理世界抽象成一个一个的Actor,Actor之间通过发送消息相互通信,Actor不关心消息是否能被接收或者能否投递到,它只是简单地投递消息给其他actor,然后等待应答。Actor相比于Coroutine是一种更高层次的抽象,它提供的receive和pattern match的原语更接近于现实世界,而使用coroutine编程你还需要手工介入任务调度,这在Actor中是由一个调度器负责的。
同样,Actor可以用coroutine实现,例如Ruby有个revactor项目,就是利用1.9引入的Fiber实现actor风格的编程,它的实现非常简单,有兴趣地可以看看,其实跟continuation实现coroutine类似。但是Actor并不是一定要用coroutine才能实现,Actor是一种编程风格,你在Java、C#、C++中同样可以模拟这样的方式去做并发编程,.net社区的老赵实现过一个简单的Actor,Scala的Actor实现是基于外部库,利用scala强大的元编程能力使得库的使用像内置于语言。
总结下我想表达的:Continuation是程序设计领域的基础概念,它可以用于实现coroutine式的多任务,Actor是一种比之coroutine更为抽象的编程风格,Actor可以基于Coroutine实现但并非必须,Actor和Coroutine都是现在比较受关注的并发模型。
文章转自庄周梦蝶 ,原文发布时间 2010-03-23
相关文章
- 使用 Amazon Redshift 通过配额机制监控及控制 schema 存储空间
- Amazon EMR Managed Scaling 介绍——自动调整集群大小,高效节约运营成本
- Amazon Redshift Federated Query 最佳实践
- 如何在 ADFS 与 AWS 之间建立信任,并通过 Active Directory 凭证配合 ODBC 驱动程序接入 Amazon Athena
- 在EMR 6.0.0 中使用 Docker 简化您的 Spark 依赖项管理
- Komodo Health 公司如何在 EKS 与 EMR 6 上使用多租户 Notebook 平台建立自助服务分析方案
- Compass 公司使用 Amazon ES 推动房屋搜索流程的简化与现代化
- AWS Wavelength 区现已在波士顿和旧金山开放
- Alexa 使用 Amazon Translate 覆盖更多国际客户
- 使用 Amazon Kendra 强化企业搜索能力
- New – Using Amazon GuardDuty to Protect Your S3 Buckets
- Java ThreadLocal详解
- 云财务管理落户 AWS 中国
- Amazon Translate 现在支持 Office 文档
- Amazon Fraud Detector 现已全面推出
- AWS DataSync 新增本地对象存储支持
- 新增功能 – 基于 AWS Graviton2 的 Amazon EC2 实例,支持本地基于 NVMe 的 SSD 存储
- 借助 NetApp CVO 实现 EDA 混合架构下的统一数据存储
- 使用分布式可用性组实现多区域 SQL Server 部署
- Amazon Kinesis Data Analytics 无服务器流式数据处理服务简介