关于协程的思考

什么是协程

昨天第一次接触这个概念,朋友说python的协程好难理解,所以研究了下什么是协程。
从Java程序员的视角可以理解为由用户代码自行控制的多线程切换。真正的线程的切换是需要由操作系统来进行控制的,而这会带来性能的开销,并且由于抢占式执行的特性,往往多线程需要手动加上各种锁来确保逻辑的执行顺序。
协程的优势网上的资料大多是针对线程来说的比如:

1.省去了cpu线程切换的开销;
2.降低了内存消耗;
3.提高了cpu缓存命中率;
4.整体上提高了性能;
5.不提高硬件的前提下,提升了系统的负载能力。

为什么协程是密集io的解决方案

这一点想了很久都不明白,协程不还是要乖乖地等着io执行完吗?
解决高密集io的一个传统方法是采用回调函数的写法,spring mvc为例,线程A接收到用户请求后交给另一个线程B去处理,线程A回归线程池,等待下一次请求进来,而线程B处理完io等操作后调用回调函数把数据返回给用户。或者在别的一些语言里,回调也是在一个线程里执行的,由解释器来控制调度。
另一个解决方案是协程,io会阻塞线程,而线程太多会因为系统的物理性能达到瓶颈,那把线程用占用资源更低的协程代替的话,自然负载能力就上去了。

协程对比主动的函数调用优势

参考廖雪峰老师的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import time
def 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)

如果改成r=c.send()改为直接调用consumer(),consumer()也改为直接返回值不也一样能运行吗?
参阅资料并思考后发现,如果consumer()中一开始有段初始化的逻辑,且恰好这段逻辑是个大io,那协程的写法这个状态是保存下来的,而函数调用则需要每次初始化一遍,性能的差别就很巨大了。