Actor、Coroutine和Continuation的概念澄清

简介:
   Actor、 CoroutineContinuation这三个概念由于并发的受关注而被经常提到,这里主要想谈下这三者的区别和联系,以便更好的区分问题领域和讨论。首先,Actor和Coroutine在我看来是两种并发模型,仅针对于并发这个领域,而Continuation则是程序设计领域的一个概念,相比于Actor和Coroutine是一个更基础的概念。

    那么,什么是Continuation?这个要从表达式的求值说起。一个表达式的求值可以分为两个阶段:“ What to evaluate?”和“ What to do with the value”,“What to do with the value”就是计算的Continuation。以下面这段代码为例:
if  x < 3  then
   
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实现一些此类高级的控制结构:

(call / cc ( lambda  ( break )
        (
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,我稍微修改了下并添加了注释:
;;continuation栈,保存了等待执行的continuation
(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))))
(注:scheme以分号开头作为注释)

     这其实就是一个coroutine的简单实现,context的保存、任务的调度、resume/yield原语……样样俱全。使用起来类似这样,下面这段程序轮流打印字符串:
(define (display-str str)
        (lambda()
         (let loop()
              (display str)
              (newline)
              (yield)
              (loop))))

;;;创建两个协程并启动调度器
(resume (display-str "This is AAA"))
(resume (display-str "Hello from BBB"))
(thread-exit)

     任务非常简单,打印下传入的字符串并换行,然后让出执行权给另一个任务执行,因此输出:
This is AAA
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社区的老赵实现过一个简单的ActorScala的Actor实现是基于外部库,利用scala强大的元编程能力使得库的使用像内置于语言。

    总结下我想表达的:Continuation是程序设计领域的基础概念,它可以用于实现coroutine式的多任务,Actor是一种比之coroutine更为抽象的编程风格,Actor可以基于Coroutine实现但并非必须,Actor和Coroutine都是现在比较受关注的并发模型。

文章转自庄周梦蝶  ,原文发布时间 2010-03-23

目录
相关文章
|
6月前
|
Linux Go SoC
关于S3学习所涉及到的知识(二):Linux设备驱动suspend/resume的顺序
关于S3学习所涉及到的知识(二):Linux设备驱动suspend/resume的顺序
261 0
|
3月前
|
Java 调度
搞清楚wait、sleep、join、yield四者区别,面试官直接被征服!
掌握上述多线程控制方法的运用,可以在Java多线程程序编写中进行更加深入的线程管理,确保程序运行更加高效、稳定。在面试中准确并熟练地讲解这些概念,确实有可能让面试官对你的专业能力留下深刻印象。
34 0
|
4月前
|
存储
向量化代码实践问题之Task<T>类中的on_completed函数是如何工作的
向量化代码实践问题之Task<T>类中的on_completed函数是如何工作的
|
6月前
|
Java 调度
一张图搞清楚wait、sleep、join、yield四者区别,面试官直接被征服!
一张图搞清楚wait、sleep、join、yield四者区别,面试官直接被征服!
56 2
|
JavaScript 前端开发 API
事件循环机制(Event Loop)的基本认知
事件循环机制(Event Loop)的基本认知
277 0
事件循环机制(Event Loop)的基本认知
|
消息中间件 前端开发 Java
Actor 模型工作机制和消息机制 | 学习笔记
快速学习 Actor 模型工作机制和消息机制
Actor 模型工作机制和消息机制 | 学习笔记
|
SQL 开发框架 安全
三分钟掌握共享内存 & Actor并发模型
吃点好的,很有必要。今天介绍常见的两种并发模型:共享内存&Actor
三分钟掌握共享内存 & Actor并发模型
|
程序员 C#
终于明白了 C# 中 Task.Yield 的用途
原文:终于明白了 C# 中 Task.Yield 的用途 最近在阅读 .NET Threadpool starvation, and how queuing makes it worse 这篇博文时发现文中代码中的一种 Task 用法之前从未见过,在网上看了一些资料后也是云里雾里不知其解,很是困扰。
1210 0
|
Python
使用Asyncio的Coroutine来实现一个有限状态机
如图: #!/usr/bin/env python # -*- coding: utf-8 -*- import asyncio import datetime import time from random import randint @asyncio.
1098 0