通常在python中设计并发编程都是使用多进程和多线程来实现的,由于GIL锁的的存在对于CPU密集型的任务通常使用多进程来实现,而对于IO密集型任务通常采用多线程来实现,让线程在调度的时候让出GIL,从而在表面上实现并发。其实对于IO密集型任务我们还可以利用协程来实现。
协程
协程,被称为微线程。协程是运行在单线程中的并发,通过程序的切换省去了线程之间切换的开销,提高了效率。asyncio异步模块就是协程模块。协程也不需要锁的机制因为协程是在同一个线程之中切换的,避免了同时访问数据的问题,依次执行效率要高得多。通过多进程+协程的方式来利用多核CPU以此提高性能。
协程实现的生产者消费者模型。
import random
import time
def producer(consumer):
next(consumer)
while True:
data=random.randint(0,99)
print("生产者生产了:",data)
consumer.send(data)
time.sleep(1)
def consumer():
while True:
item=yield
print("消费者消费了:",item)
if __name__ == '__main__':
c=consumer()
p=producer(c)
yield
yield关键字在python中用来制造生成器,也就是说带有yield关键字的函数就是生成器函数,yield执行时会在yield处停住将yield后面的表达式返回(默认None),直到被next()方法调用才会继续执行。当没有办法yield时就会抛出异常。除了用next()语法,for循环遍历也是可以取到所有值。具体内容可参考以前的文章。生成器。
send
send的语法可以让yield接收外部发来的值,这样一个生成器就变成了协程。每个生成器都可以执行send()方法为yield 发送数据,此时yield不但可以暂停并返回函数,还可以接受send传过来的值并重新激活函数,将值赋给一个变量x(x = yield xxxxx)。
def func(): i=0 while True: x=yield i i+=1 print("第{}次执行fun函数,x的值时{}".format(i,x)) g=func() next(g) g.send("hello") g.send("python") >>>>第1次执行fun函数,x的值时hello 第2次执行fun函数,x的值时python
协程可以处于下面四个状态中的一个。当前状态可以导入inspect模块,使用inspect.getgeneratorstate(...) 方法查看,该方法会返回下述字符串中的一个。
-
'GEN_CREATED' 等待开始执行。
-
'GEN_RUNNING' 协程正在执行。
-
'GEN_SUSPENDED' 在yield表达式处暂停。
-
'GEN_CLOSED' 执行结束。
@asyncio.coroutine和yield from
在python3.4开始asyncio加入python的标准库中。可以通过此来轻松的完成协程实现的一部IO操作。asyncio是一个基于事件循环的异步IO模块,通过yield from
,我们可以将协程asyncio.sleep()
的控制权交给事件循环,然后挂起当前协程;之后,由事件循环决定何时唤醒asyncio.sleep
,接着向后执行代码。@asyncio.coroutine用于将函数声明为协程。yield from就是等待另一个函数的返回。
import asyncio import datetime @asyncio.coroutine # 声明一个协程 def display_date(num, loop): while True: print("任务{}完成,time:{}".format(num,datetime.datetime.now())) yield from asyncio.sleep(2) # 阻塞直到协程sleep(2)返回结果 loop = asyncio.get_event_loop() # 获取一个event_loop tasks = [display_date(1, loop), display_date(2, loop)] loop.run_until_complete(asyncio.gather(*tasks)) # "阻塞"直到所有的tasks完成 loop.close() >>> 任务1完成,time:2018-09-23 14:16:12.091581 任务2完成,time:2018-09-23 14:16:12.092156 任务1完成,time:2018-09-23 14:16:14.093619 任务2完成,time:2018-09-23 14:16:14.094123 任务1完成,time:2018-09-23 14:16:16.097316 任务2完成,time:2018-09-23 14:16:16.097562 任务1完成,time:2018-09-23 14:16:18.099375 任务2完成,time:2018-09-23 14:16:18.100089
async、await
在python3.5中,引入async和await 关键字。让协程独立于生成器而存在。
import asyncio import datetime # @asyncio.coroutine # 声明一个协程 async def display_date(num, loop): while True: print("任务{}完成,time:{}".format(num,datetime.datetime.now())) # yield from asyncio.sleep(2) # 阻塞直到协程sleep(2)返回结果 await asyncio.sleep(2) loop = asyncio.get_event_loop() # 获取一个event_loop tasks = [display_date(1, loop), display_date(2, loop)] loop.run_until_complete(asyncio.gather(*tasks)) # "阻塞"直到所有的tasks完成 loop.close()
asyncio模块
asyncio的使用主要分为创建事件循环、指定循环模式并运行、关闭循环。
通常使用asyncio.get_event_loop()创建事件循环。运行循环有两种方法:一是调用run_until_complete()
方法,二是调用run_forever()
方法。前者内置add_done_callback 回调函数,后者则可以自定义此函数。
import asyncio async def func(future): await asyncio.sleep(1) future.set_result("执行!") if __name__ == '__main__': loop=asyncio.get_event_loop() future=asyncio.Future() asyncio.ensure_future(func(future)) print(loop.is_running()) loop.run_until_complete(future) print(future.result()) loop.close()
import asyncio async def func(future): await asyncio.sleep(1) future.set_result("执行!") def call_result(future): print(future.result()) loop.stop() if __name__ == '__main__': loop=asyncio.get_event_loop() loop = asyncio.get_event_loop() future = asyncio.Future() asyncio.ensure_future(func(future)) future.add_done_callback(call_result) try: loop.run_forever() finally: loop.close()
greenlet模块
greenlet为第三方库,使用前先安装pip install greenlet。
这个库为以前的python中,当然python3中提供了前面所讲的更好的方法。该模块也可以让函数变成协程。
from greenlet import greenlet import random import time def producer(): while True: data=random.randint(0,99) print("生产者生产了",data) c.switch(data) time.sleep(1) def consumer(): while True: item=p.switch() print("消费者消费了",item) p=greenlet(producer) c=greenlet(consumer) c.switch()
gevent
gevent封装了libev(基于epoll)和greenlet两个库。遇到阻塞就会切换。既没有轮询的开销也没有线程的开销。
import time import random import gevent from gevent.queue import Queue queue=Queue(2) def producer(queue,): while True: data=random.randint(0,99) print('生产者生产了:',data) queue.put(data) time.sleep(1) def consumer(queue): while True: data=queue.get() print("消费者消费了:",data) if __name__ == '__main__': p=gevent.spawn(producer,queue) c = gevent.spawn(consumer, queue) gevent.joinall([p,c])