协程
1.协程,又称微线程,纤程。英文名Coroutine。
协程看上去是函数,但执行过程中,在函数内部可中断,然后转而执行别的函数,在适当的时候再返回来接着执行。
python可以通过 yield/send 的方式实现协程,也可以使用第三方库中的greenlet来实现协程。
2.协程的优势
协程的特点在于是一个线程执行。
所以协程最大的优势就是极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。*
生产者-消费者模型
#消费者
def consumer():
r = ''
while True:
n = yield
print('[CONSUMER] Consuming %s...' % n)
#生产者
def produce(c):
c.send(None)
for n in range(1, 6):
print('[PRODUCER] Producing %s...' % n)
r = c.send(n)
c.close()
c = consumer()
produce(c)
使用greenlet实现生产者/消费者模型
from greenlet import greenlet
def consumer():
while True:
n = gr_produce.switch()
print('[CONSUMER] Consuming %s...' % n)
def produce():
for n in range(1, 6):
print('[PRODUCER] Producing %s...' % n)
gr_consumer.switch(n)
gr_consumer = greenlet(consumer)
gr_produce = greenlet(produce)
#切换到consumer中运行
gr_consumer.switch()
协程的缺陷
1.无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上。当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
2.进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序。
同步IO/异步IO
同步IO操作:导致请求进程阻塞,直到IO操作完成。
异步IO操作:不导致进程阻塞。
python实现异步IO的库:
gevent
asyncio
gevent
greenlet已经实现了协程,但是这个还的人工切换,是不是觉得太麻烦了,不要捉急,python还有一个比greenlet更强大的并且能够自动切换任务的模块gevent
其原理是当一个greenlet遇到IO(指的是input output 输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。
由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO
安装方式
使用如下命令安装gevent模块:
pip3 install gevent
gevent的基本使用方法
*gevent.spawn(func, args, …)方法用来生成协程,他接受一个函数作为参数
gevent.joinall([t1, t2, …])方法用来启动协程轮询并等待运行结果
gevent.sleep()用来模拟一个IO操作,阻塞后自动切换到另一个协程执行
实例代码-遇到IO阻塞自动切换
import gevent
def task1():
print('[TASK1] Begin')
gevent.sleep(8)
print('[TASK1] End')
def task2():
print('---[TASK2] Begin')
gevent.sleep(3)
print('---[TASK2] End')
def task3():
print('------[TASK3] Begin')
print('------[TASK3] End')
gevent.joinall([
gevent.spawn(task1),
gevent.spawn(task2),
gevent.spawn(task3)
])
asyncio
asyncio是Python 3.4版本引入的标准库,直接内置了对异步IO的支持。
asyncio的编程模型包含两个重要的点:
coroutines(协程):交给 asyncio 执行的任务,称为协程(coroutine)。
event loop(事件循环):然后把需要执行的协程放到event loop中执行, event loop负责监测和事件触发等处理工作。当某一协程遇到阻塞等待读取或者写入数据的时候, 进行上下文的切换,把运行机会让给其他协程,从而让执行效率最大化。
asyncio编程模型
协程的定义,需要使用 async def 语句。
async def do_some_work(x):
asyncio.sleep(x)
asyncio.sleep(x) 用来模拟实际的IO操作,await asyncio.sleep(x) 就是等待这个IO操作的结束,这个过程会发生阻塞,从而导致协程切换。
协程定义好了之后还需要使用 ensure_future() 函数把协程对象包装成了future对象
asyncio.gather将多个Future进行封装成一个Future队列
主程序中通过 asyncio.get_event_loop() 得到一个标准的事件循环
时间循环提供一个 run_until_complete() 方法,它接收asyncio.gather封装好的Future队列来运行 loop ,直到所有的future完成
实例代码 - 遇到阻塞自动切换
import asyncio
async def task1():
print('[TASK1] Begin')
await asyncio.sleep(8)
print('[TASK1] End')
async def task2():
print('---[TASK2] Begin')
await asyncio.sleep(3)
print('---[TASK2] End')
async def task3():
print('------[TASK3] Begin')
print('------[TASK3] End')
tasks = asyncio.gather(asyncio.ensure_future(task1()),
asyncio.ensure_future(task2()),
asyncio.ensure_future(task3()))
loop = asyncio.get_event_loop()
loop.run_until_complete(tasks)
loop.close()