子程序或者说函数之间的相互嵌套调用是通过栈来实现的,一个线程就是执行一个子程序,子程序调用总是一个入口对应一次返回,调用的顺序是事先确定的,在一个子程序的执行过程中不能中断去执行其他子程序,而协程则可以在子程序内部中断(类似cpu的中段机制)转而去执行其他子程序(⚠️不是函数调用),在适当的时候再返回执行。
协程的特点是一个线程运行,和多线程比协程的优势在哪里?
1 没有切换线程时的开销;
2 因为时在一个线程里运行,所以不需要‘锁’同步协程;
我们改用协程来实现经典的生产者消费者模式:
>>> 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)
启动生成器; -
然后,一旦生产了东西,通过
c.send(n)
切换到consumer
执行; -
consumer
通过yield
拿到消息,处理,又通过yield
把结果传回; -
produce
拿到consumer
处理的结果,继续生产下一条消息; -
produce
决定不生产了,通过c.close()
关闭consumer
,整个过程结束。
整个流程无锁,由一个线程执行,produce
和consumer
协作完成任务,所以称为“协程”,而非线程的抢占式多任务。
最后套用Donald Knuth的一句话总结协程的特点:
“子程序就是协程的一种特例。”