协程,又称微线程,纤程。英文名Coroutine。
协程的概念由来已久,只是近几年才在一些语言如Lua中得到广泛应用。
子例程或函数在所有语言中都是按层次结构调用的。比如A调用B,B在执行过程中调用C,C执行完返回,B执行完返回,最后A执行完毕。
所以子程序调用是通过栈实现的,一个线程执行一个子程序。
子程序调用总是一进一出,调用顺序明确。协程的调用不同于子例程。
协程看起来也像一个子程序,但是在执行过程中,可以在子程序内部中断,然后转而去执行其他的子程序,在合适的时候再返回继续执行。
注意在一个子程序中打断去执行其他子程序不是函数调用,有点类似于CPU中断。例如,子程序 A 和 B:
def A():
print '1'
print '2'
print '3'
def B():
print 'x'
print 'y'
print 'z'
假设它是由协程执行的,在执行A的过程中,可以随时中断执行B,执行过程中也可能中断B,然后执行A。结果可能是:
1
2
x
y
3
z
但是在A中,并没有调用B,所以协程的调用比函数调用更难理解。
看起来A和B的执行有点像多线程,但是coroutine的特点是一个线程执行。与多线程相比,协程有什么优势?
优点是协程的执行效率极高。因为子程序切换不是线程切换,而是由程序自己控制的,所以没有线程切换的开销。与多线程相比,线程越多,协程的性能优势越明显。
第二大优点是不需要多线程的锁机制,因为只有一个线程,同时写变量不存在冲突。在协程中,对共享资源进行无锁控制,只需要判断状态即可,因此执行效率比很多线程高很多。
因为协程是一个线程执行的,如何使用多核CPU呢?最简单的方法就是多进程+协程,既充分利用了多核,又充分发挥了协程的高效性,可以获得极高的性能。
Python对协程的支持还是很有限的,生成器中使用的yield可以在一定程度上实现协程。虽然支撑还不完整,但已经可以发挥出不小的威力了。
来看例子:
传统的生产者消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁。
如果换成协程,producer生产消息后,直接通过yield跳转到consumer开始执行。消费者执行完后,切换回生产者继续生产,效率极高:
import timedef consumer():
r = ''
while True:
n = yield r if not n: return
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)
执行结果:
注意到consumer函数是一个generator(生成器),把一个consumer传入produce后:
首先调用c.next()启动生成器;
然后,一旦生产了东西,通过c.send(n)切换到consumer执行;
consumer通过yield拿到消息,处理,又通过yield把结果传回;
produce拿到consumer处理的结果,继续生产下一条消息;
produce决定不生产了,通过c.close()关闭consumer,整个过程结束。
整个过程是无锁的,一个线程执行。生产者和消费者协作完成任务,所以称为“协程”而不是线程抢占式多任务。
最后套用Donald Knuth的一句话总结协程的特点:
“子程序就是协程的一种特例。”
本文为原创文章,版权归知行编程网所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ python迭代器和生成器有什么区别08/17
- ♥ python函数和方法有什么区别08/19
- ♥ python下如何写脚本01/09
- ♥ python安装后无法运行怎么办09/26
- ♥ 如何在python中安装新模块09/11
- ♥ 日历如何判断python3时间?01/02
内容反馈