在学习异步IO模型前,先来了解协程

协程又叫做微线程,Coroutine

子程序或者成为函数,在所有语言中都是层级调用,比如a调用b,b调用c。c执行完毕返回,b执行完毕返回,最后a执行完毕返回

所以子程序是通过栈来实现的,一个线程就是执行一个子程序

子程序调用总是一个入口一次返回,调用顺序是明确的,而协程的调用和子程序是不同的。

协程看上去是子程序,但在执行过程中可以在子程序内部中断,转而执行别的子程序,在适当的时候返回执行

注意在一个子程序中断去执行其他子程序不是函数调用,类似于CPU的中断,比如子程序A,B

def A():
print('')
print('')
print('') def B():
print('x')
print('y')
print('z')

假设由协程执行,在执行A的过程中,可以随时中断,去执行B,B也可能在执行过程中中断再去执行A,结果可能是:

1
2
x
y
3
z

但是在A中是没有调用B的,所以协程的调用比函数理解要难一些

看起来A,B的执行像是多线程,但协程的特点是在于一个线程执行,和多线程相比,有什么优势

最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身来控制,因此没有现成切换的开销,线程数量越多协程性能优势越明显

第二个优势就是不需要多线程的锁机制,因为只有一个线程,也不存在写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,

所以执行效率比多线程高很多

因为协程是一个县城执行,那么利用多核CPU你,最简单的办法就是多进程+协程,即充分利用多核,又充分发挥协程的高效率,获得极高的性能,

python多协程的支持是通过generator实现的。

在generator中,我们不但可以通过for循环来迭代,还可以不断调用next()获取yield语句返回的下一个值

但是python的yield不但可以返回一个值,它还可以接受调用者发出的参数

来看例子:

传统的生产者消费者模型是一个线程写消息一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁

如果改用协程,生产者生产消息后,直接通过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)
[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

n = yield r 并不是赋值语句,相当于:

if produce return n
if consume return r

如果是produce调用send的话,就相当于在consumer中为n赋值 如果是consume调用yield r的话,就相当于在produce中为r赋值

c.send(None) 用于启动一个生成器,如果参数不传None的话会报错。 但是也只用于启动生成器,并不会调用yield 也就是说,当执行send(None) 从produce转到consume时,只执行到yield r的上一句


注意到consumer函数是一个generator,把一个consumer传入produce后:

  1. 首先调用c.send(None)启动生成器;

  2. 然后,一旦生产了东西,通过c.send(n)切换到consumer执行;

  3. consumer通过yield拿到消息,处理,又通过yield把结果传回;

  4. produce拿到consumer处理的结果,继续生产下一条消息;

  5. produce决定不生产了,通过c.close()关闭consumer,整个过程结束。

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

最后套用Donald Knuth的一句话总结协程的特点:

“子程序就是协程的一种特例。”

最新文章

  1. 利用Ramdisk为Firefox 加速
  2. [Git].gitignore失效的原因
  3. GCC 4.8.2 编译安装
  4. UML: 部署图
  5. Python 进阶(五)定制类
  6. 快速生成R语言报告(markdown+Rstudio)
  7. java中子类与基类变量间的赋值
  8. 分布式助手Zookeeper(四)
  9. Java基础知识强化之IO流笔记49:IO流练习之 复制指定目录下指定后缀名的文件并修改名称的案例
  10. Tomcat 配置成https协议
  11. input type=&quot;file&quot;去掉取消默认原来选择的文件
  12. [Oracle]LogMiner工具小结
  13. seajs的那些坑
  14. 集美大学网络1413第十二次作业成绩(个人作业3) -- Alpha阶段个人总结
  15. JAVA 并发编程学习(2)之基本概念
  16. eclipse中svn插件的工程不能与svn资源库同步的解决方法
  17. 洛谷 T28312 相对分子质量【2018 6月月赛 T2】 解题报告
  18. apache安装mod_ssl.so 出现 undefined symbol: ssl_cmd_SSLPassPhraseDialog错误解决
  19. EXCEL数据匹配:The &#39;Microsoft.Jet.Oledb.4.0&#39; provider is not registered on the local machin
  20. Object之equals和hashCode

热门文章

  1. 常用的python模块及安装方法
  2. 剑指offer-反转链表15
  3. NLP系列-中文分词(基于词典)
  4. 学习bash——通配符与特殊符号
  5. shell 中的expect 用法
  6. 简单dp总结
  7. 小C的记事本(栈记录字符串)
  8. [剑指Offer] 43.左旋转字符串
  9. kali linux下的常用bash命令
  10. PowerShell收发TCP消息包