协程Coroutine,在执行时,可以中断,转而运行其他协程,且在适当的时候,又可以返回原有子程序继续执行。
coroutine 协程 VS subroutine 子程
子程序的调用,只有一个入口,一次退出。而协程不同,一个协程退出执行转而去执行别的协程,而别的协程执行到一定条件可以返回原先的协程去继续执行。
从程序的表现上,协程有点像线程切换。但协程的特点是在一个线程内。协程之间的切换是由程序自身控制,而不是线程切换。因此,没有锁和线程切换的开销,性能较线程更好。
子程序 是通过堆栈来执行流。而coroutine采用Continuation,Continuation利用了当前的堆栈帧和指令指针,并将其封装到一个可调用的实体(continuation)中,并使用这个 continuation 作为惟一的参数调用另外一个函数。continuation 是一个可调用实体,它只可以接收一个参数,这个参数然后会返回到创建这个 continuation 的地方。
这样的调用方式最大的好处就是,它可以让你从任意一个节点跳到另一个节点。而不必遵循堆栈方式的一层一层的return方式。比如说,在当前的函数内,你只要有一个其它函数的节点信息,完全可以选择return到那个函数,而不是循规蹈矩地返回到自己的调用者。你也可以在一个函数的任何位置储存自己的上下文信息,然后,在以后某个适当的时刻,从其它的任何一个函数里面返回到自己现在的位置。
python 中协程使用
Python对协程的支持是通过generator实现的。Python的yield不但可以返回一个值,它还可以接收调用者发出的参数。
来看下生产消费者的一个例子
def consumer():
r = ''
while True:
n = yield r
if not n:
return
print('[CONSUMER] Consuming %s...' % n)
r = '200 OK'
def produce(c):
c.send(None)
n = 0
while n < 5:
n = n + 1
print('[PRODUCER] Producing %s...' % n)
r = c.send(n)
print('[PRODUCER] Consumer return: %s' % r)
c.close()
c = consumer()
produce(c)
执行结果:
[PRODUCER] Producing 1...
[CONSUMER] Consuming 1...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 2...
[CONSUMER] Consuming 2...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 3...
[CONSUMER] Consuming 3...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 4...
[CONSUMER] Consuming 4...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 5...
[CONSUMER] Consuming 5...
[PRODUCER] Consumer return: 200 OK
注意到consumer函数是一个generator,把一个consumer传入produce后:
首先调用c.send(None)启动生成器,consumer执行到到yield,返回producer执行
然后,producer一旦生产了东西,通过c.send(n)切换到consumer执行;
consumer通过yield拿到消息,处理,又通过yield把结果传回;
produce拿到consumer处理的结果,继续生产下一条消息;
produce决定不生产了,通过c.close()关闭consumer,整个过程结束。