抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

关键词

coroutine、yield、next、send

协程(coroutine)

协作式多任务的子程序,允许执行被挂起与被恢复。相对于子程序而言,协程更为通用和灵活。

协程可以通过yield来调用其他协程,接下来的每次协程被调用时,从协程上次yield返回的位置接着执行,通过yield方式转移执行权的协程之间不是调用者与被调用者的关系,而是彼此对称、平等的。

协程与子进程的对比

  1. 子进程可以调用其他子进程,调用者等待被调用者结束后继续执行,子进程的生命期遵循后进先出,即最后一个被调用的子进程最先结束返回。协程的生命期完全由对它们的使用需要来决定。
  2. 子进程的起始处是唯一的入口点,每当子进程被调用时,执行都从被调用的子进程的起始处开始。协程可以有多个入口点,协程的起始处是第一个入口点,每个yield返回出口点都是再次被调用执行时的入口点
  3. 子进程只在结束时一次性的返回全部结果值。协程可以在yield时不调用其他协程,而是每次返回一部分的结果,这种协程称作生成器或者迭代器
  4. 现代的指令集架构通常提供对调用栈的指令支持,便于实现可递归调用的子进程。在提供续体的语言环境下,恰好可用控制状态抽象表示来实现协程

伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var q := new 队列

coroutine 生产者
loop
while q 不满载
建立某些新产品
向 q 增加这些产品
yield 消费者
coroutine 消费者
loop
while q 不空载
从 q 移除某些产品
使用这些产品
yield 生产者

同线程比较

协程非常类似与线程,但是协程是协作式多任务的,典型的线程是内核级抢占式多任务的。协程提供并发行而非并行性,协程超过线程的好处是它们可以用于硬性实时的语境(在协程之间的切换不需要涉及任何系统调用或者任何阻塞调用),这里不需要守卫关键区块的同步性原语(primitive)如互斥锁、信号量等,并且不需要来自操作系统的支持。有可能以一种对调用代码透明的方式,使用抢占式调度的线程实现协程,但会失去某些优势(特别是对硬性实时操作系统的适合性和相对廉价的相互之间切换)

用户级线程是协作式多任务的轻量级线程,概念上上和协程一样。区别在于协程是语言级别的狗仔,可以看作是一种流程控制,而线程是系统层级的构造。

生成器

生成器也叫半协程,是协程的子集。尽管二者都可以yield多次,暂停suspend自身的执行,并允许在多个入口点重新进入,但是它们的差异在于,协程有能力控制在它让位之后哪个协程立即续接它来执行,而生成器不能,他只能把控制权转交给调用者。在生成器的yield语句不指定要跳转到的协程,而是向父进程传递返回值.

可以在生成器设施之上实现协程,这需要通过顶层的分派器历程的援助,它显示的把控制权传递给生成器传回的记号所指定的另一个生成器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var q := new 队列

generator 生产者
loop
while q 不满载
建立某些新产品
向 q 增加这些产品
yield 消费者
generator 消费者
loop
while q 不空载
从 q 移除某些产品
使用这些产品
yield 生产者

subroutine 分派器
var d := new 字典(生成器 -> 迭代器)
d[生产者] := start 生产者
d[消费者] := start 消费者
    var 当前 := 生产者
    loop
        call 当前
        当前 := next d[当前]

call 分派器

生成器和迭代器的用法有着微妙的差异,生成器看起来像是函数而表现得像是迭代器。在python中生成器是迭代器的子构造,他是返回迭代器的函数

引用:wikipedia-协程

评论