什么是协程?
knuth解释:协程就是多入多出的子程序,就是可以协同运行的程序。
通常在Python中我们进行并发编程一般都是使用多线程或者多进程来实现的。
对于计算型任务由于GIL的存在我们通常使用多进程来实现;而对于IO型任务我们可以通过线程调度来让线程在执行IO任务时让出GIL,从而实现表面上的并发。
- Python3.4中加入了asyncio
- Python3.5上提供了async/await语法层面的支持
- Python3.6中asyncio已经由临时版改为了稳定版
对于IO型任务我们还有一种选择就是协程,协程是运行在单线程当中的“并发”,协程相比多线程的优势:
- 省去了多线程之间的切换开销,获得了更大的运行效率。
- 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了。
两者从系统层面来说:
- 协程是自己程序调度的,逻辑上并行执行,底层上非并行执行
- 线程是操作系统调度的,逻辑和底层上都是并行执行
Python的协程是基于generator实现的,Python中的asyncio也是基于协程来进行实现的。
在generator中,我们不但可以通过for循环来迭代,还可以不断调用next()函数获取由yield语句返回的下一个值。但是Python的yield不但可以返回一个值,它还可以接收调用者发出的参数。
使用协程
Python中的协程大概经历了如下三个阶段:
- 最初的生成器变形yield/send
- 引入@asyncio.coroutine 和 yield from
- 在最近的Python3.5版本中引入async/await关键字
生产者与消费者
示例代码1
1 | def consumer(): |
输出结果1
2
3
4
5
6
7
8
9
10
11
12
13
14
15produce() producing 1
consumer() 1
produce() receive msg from consumer: ok
produce() producing 2
consumer() 2
produce() receive msg from consumer: ok
produce() producing 3
consumer() 3
produce() receive msg from consumer: ok
produce() producing 4
consumer() 4
produce() receive msg from consumer: ok
produce() producing 5
consumer() 5
produce() receive msg from consumer: ok
实际执行过程如下:
- 在produce(),cmer.send(None)启动生成器
- 在produce(),通过cmer.send(n)切换到consumer()执行
- 在consumer(),通过n = yield r_msg拿到消息,处理。最后再通过yield r_msg把结果传回
- 在produce(),r_msg = cmer.send(n)拿到consumer处理的结果,继续生产下一条消息
- 在produce(),cmer.close()决定停止生产
yield from
示例代码1
1 | def gen_yield(): |
输出结果1
2
3
4
5
6
7range(0, 5)
====================
0
1
2
3
4
两者的区别:yield直接将range这个generator返回,而yield from解析range,将每个item返回。
特别注意:yield from后面必须跟iterable对象(可以是生成器,迭代器)。
asyncio.coroutine
示例代码1 yield from
1 | def fab(max): |
输出结果1
2
3
4
51
1
2
3
5
示例代码2 asyncio
1 |
|
输出结果1
2
3
4
5
6
7
8
9
10
11Stupid think 0.08516295175908538 secs to get 1
Smart think 0.1537956191924291 secs to get 1
Smart think 0.12929758554701115 secs to get 1
Stupid think 0.2892256222928606 secs to get 1
Smart think 0.17164449165156828 secs to get 2
Smart think 0.16220980840025945 secs to get 3
Stupid think 0.27210541712117337 secs to get 2
Smart think 0.1715009248020697 secs to get 5
Stupid think 0.2023469563932653 secs to get 3
Stupid think 0.3009223257835448 secs to get 5
All fib finished.
async 和 await
示例代码1
1 | async def smart_fib(n): |
输出结果1
2
3
4
5
6
7
8
9
10Smart think 0.00882750921810851 secs to get 1
Smart think 0.04897605243502872 secs to get 1
Stupid think 0.14133676443250204 secs to get 1
Smart think 0.09460081340195459 secs to get 2
Smart think 0.10392360738913782 secs to get 3
Stupid think 0.15655241126836938 secs to get 1
Stupid think 0.07286491653845838 secs to get 2
Smart think 0.18776425866569166 secs to get 5
Stupid think 0.1964418153612619 secs to get 3
Stupid think 0.010708925895951162 secs to get 5