python 协程 coroutine 生产者-消费者模型图示

看了廖雪峰老师关于协程的教程,为了便于理解,用 axure 做了以下图示,对于初学者应该更容易理解一些:

  1. 函数是: A调用B B调用A,有统一的出入口。入口是参数,出口是 return 协程不同。
  2. 协程是:程序A执行过程中暂时中断,把CPU控制权交给 B ,B执行一段后,再切换回A ,A从中断的地方继续执行。
  3. 相比多线程,协程的优势:程序控制切换,不需要线程切换的开销,不需要锁机制,多cpu 多进程+协程可提供极高的并发性能。
  4. 协程的实现:Python对协程的支持是通过generator实现的。
  5. 在generator中,我们不但可以通过for循环来迭代,还可以不断调用next()函数获取由yield语句返回的下一个值。
  6. 但是Python的yield不但可以返回一个值,它还可以接收调用者发出的参数(通过gen.send())。
传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁。如果改用协程,生产者生产消息后,直接通过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)

图示:

  1. 注意到consumer函数是一个generator,把一个consumer传入produce后:
  2. 首先调用c.send(None)启动生成器;
  3. 然后,一旦生产了东西,通过c.send(n)切换到consumer执行;
  4. consumer通过yield拿到消息,处理,又通过yield把结果传回;
  5. produce拿到consumer处理的结果,继续生产下一条消息;
  6. produce决定不生产了,通过c.close()关闭consumer,整个过程结束。

整个流程无锁,由一个线程执行,produce和consumer协作完成任务,所以称为“协程”,而非线程的抢占式多任务。