先记住一句话,子程序是协程的一种特例。
Python yield
Python 中的 yield 保存一个 generator 函数的状态,generator 是一个特殊类型的迭代器(iterator)
import time
def consumer():
r = ''
while True:
n = yield r
print('[CONSUMER] Consuming %s...' % n)
time.sleep(1)
r = '200 OK'
def produce(c):
c.next()
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()
if __name__=='__main__':
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
yield 相当于 return,主动交出程序的控制权,只不过它的状态保存到了内存中,还会再把控制权要回来的。第一次调用 consumer().next() 时会运行到 consumer 的 yield 这一行,然后权限又跑到了 producer 中。运行到 producer send 这一行,把权限给 consumer。consumer 和 producer 之间通过 yield 和 send 轮换掌握控制权。这样就实现了非线程方式的抢占式多任务。
next() 和 send() 调用是一样的,next() 不能传参数。
微信协程库 libco
libco 代码见 github,支持的功能
1.协程的创建与调度,使用 co_create,co_resume,co_yield 来搭建协程服务。和 Python 的 yield 方式很类似
2.基于网络的协程调度框架,可以通过网络事件来进行协程调度
libco 可以将系统的同步操作异步化,如标准 read 函数读取文件,系统打开文件描述符,阻塞在这里,一旦读取完毕返回。异步化就是调用 read 后直接返回,设置一个函数来处理读取完毕后返回的数据。把事件(读取完数据)注入到 epoll 中,同时 yield 交出线程控制权,一旦事件到来,切换到这个协程进行处理。
通过 hack 的方式替换掉了 read 函数,同理也可以替换掉和网络读写有关的接口,这样可以适用于 RPC 框架,这就是 libco 的主要思想。