协同程序与连续程序与生成器

协程、延拓和生成器之间的区别是什么?

36010 次浏览

协同程序是几个程序之一,轮流做他们的工作,然后暂停给予控制的其他协同程序在组。

Continution 是传递给某个过程的“指向函数的指针”,在该过程完成时执行。

发电机(在。NET)是一种语言结构,它可以输出一个值,“暂停”方法的执行,然后在请求下一个值时从同一点继续执行。

我会从发电机开始,因为它们是最简单的情况。正如@zvolkov 提到的,它们是可以重复调用而不返回的函数/对象,但是当被调用时将返回(生成)一个值,然后挂起它们的执行。当他们再次被调用时,他们将从上次暂停执行的地方重新开始,再次执行他们的任务。

发生器本质上是一个削减(非对称)协程。协同程序和生成器之间的区别在于,协同程序可以在最初调用之后接受参数,而生成器不能。

想出一个使用协程的简单示例有点困难,但这里是我最好的尝试。以这段(编写的) Python 代码为例。

def my_coroutine_body(*args):
while True:
# Do some funky stuff
*args = yield value_im_returning
# Do some more funky stuff


my_coro = make_coroutine(my_coroutine_body)


x = 0
while True:
# The coroutine does some funky stuff to x, and returns a new value.
x = my_coro(x)
print x

使用协程的一个例子是 lexer 和解析器。没有语言中的协程或者模拟,词法分析和解析代码需要混合在一起,即使它们实际上是两个独立的问题。但是使用协同程序,您可以将词法分析和解析代码分离出来。

(我将略过对称协程和非对称协程之间的区别。可以说它们是等价的,你可以从一个转换到另一个,非对称协程——最像生成器——更容易理解。我概述了如何在 Python 中实现非对称协程。)

延续实际上是相当简单的野兽。它们都是代表程序中另一个点的函数,如果你调用它,它会导致执行自动切换到函数所代表的点。你每天都在使用非常受限的版本,甚至都没有意识到。例如,异常可以被认为是一种由内而外的延续。我将给出一个基于 Python 的延续伪代码示例。

假设 Python 有一个名为 callcc()的函数,这个函数有两个参数,第一个是函数,第二个是用来调用它的参数列表。对该函数的唯一限制是,它接受的最后一个参数将是一个函数(这将是我们当前的延续)。

def foo(x, y, cc):
cc(max(x, y))


biggest = callcc(foo, [23, 42])
print biggest

会发生的情况是,callcc()依次调用具有当前延续(cc)的 foo(),即对程序中调用 callcc()的点的引用。当 foo()调用当前延续时,本质上就相当于告诉 callcc()返回你调用当前延续时使用的值,当它这样做时,它会回滚堆栈到创建当前延续的地方,也就是说,当你调用 callcc()时。

所有这一切的结果将是我们假设的 Python 变体将打印 '42'

我希望这有所帮助,我相信我的解释可以得到很大的改进!

在较新版本的 Python 中,您可以使用 generator.send()将值发送到 Generators,这使得 Python Generators 有效地协同工作。

Python Generator 与其他生成器(比如 greenlet)的主要区别在于,在 python 中,yield value只能返回给调用者。在 greenlet 中,target.switch(value)可以将您带到一个特定的目标协同程序,并产生一个值,target将继续运行。