Python协程与异步编程简述
- 前言
- 一、异步与协程
- 二、协程的实现方式
- 1.yield
- 2.greenlet
- 3.gevent
- 4.asyncio
- 5.async + await 关键字
- 总结
前言
Python作为一门脚本语言,经常用于IO密集型的场合,所以,对于异步编程就有所要求。在Python里,处理多任务有三种方式:1.多线程 2.多进程 3.协程。 多线程是轻量级的多任务方式,但是由于GIL(全局解释器锁)导致其在Cpython下性能不能发挥出来,而且虽然有GIL,多线程也是有着线程安全的问题。而多进程占用的系统资源较多。协程作为一种用户态上下文切换方式的优点就体现出来了,它是轻量级的,比多线程占用的资源更少,但是执行速度却一点不慢。
一、异步与协程
说起异步就不得不说同步,在计算机领域内,**同步指的是如果一个进程在执行一个请求时,如果该请求需要等待一段时间才能返回信息,则改进程则会一直等待下去;异步则是进程不需要一直等待下去,而是转而去执行一些其他的操作。**在生活中异步的概念就相当于,假如你有一个洗衣服的任务,你把衣服准备好,然后放进洗衣机里,洗衣机洗衣服需要30分钟,而这段时间如果你是同步的,则你就站洗衣机旁边等,等它洗完了,然后把衣服拿出来晾干。异步的操作就是,在洗衣机洗衣服的时候,你去洗个碗,拖个地,等到听见“蹬”的一声,衣服洗好了,你再去把它拿出来晾干。
从上面的例子你可以看出,如果我们想异步洗衣服的时候你可以去干其他的事情,这说明,对于这种多任务,多等待(IO操作)的任务,使用异步的方式可以榨干性能。而且,从上面的例子可以看出,在进行异步操作的时候,我们需要保存任务的状态,保证在我们返回查看的任务的时候能返回,即你洗衣服的时候得确认你今天是去自家洗衣机洗的衣服,而不是丢给洗衣店洗的。在Python中,协程就是通过异步操作来实现并发的多任务的方式。
二、协程的实现方式
1.yield
yield方式本质上是一种创建生成器的方式。
import time
def task1():
while True:
print("----task1----")
yield
time.sleep(1)
def task2():
while True:
print("----task2----")
yield
time.sleep(1)
def main():
t_start = time.time()
for i in range(10000):
next(task1())
next(task2())
t_end = time.time()
print("时间:%s" % (t_end - t_start))
if __name__ == '__main__':
main()
2.greenlet
对yield的功能进行了封装,但切换任务的时候需要手动切换,在后面被gevent发扬。此处不赘述,想要了解可以搜索。
3.gevent
对greenlet的功能进行了进一步的封装,在遇到IO操作的时候自动切换任务,缺点是操作必须用自己的模块,后续打了monkey补丁后解决了这个问题。
# gevent实现协程(需要自己下)
import gevent
from gevent import monkey
import time
def task1(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(1)
if __name__ == '__main__':
monkey.patch_all()
gevent.joinall([
gevent.spawn(task1, 5),
gevent.spawn(task1, 5),
gevent.spawn(task1, 5)
])
4.asyncio
asyncio是python3以后推荐的协程创建方式。
import asyncio
@asyncio.coroutine
def fun1():
print(111)
yield from asyncio.sleep(1)
print(222)
@asyncio.coroutine
def fun1():
print(333)
yield from asyncio.sleep(1)
print(444)
# 创建事件循环
loop = asyncio.get_event_loop()
# 把任务添加至事件循环中并执行
loop.run_until_complete(fun1())
# python3.5后把上面两句可以合并为 asyncio.run()
5.async + await 关键字
python3.5后更新的方式,比asyncio更加简洁
import asyncio
import time
async def test(n, i):
for j in range(n):
print("work%s is runing" % i)
await asyncio.sleep(1)
return i
roll_list = [i for i in range(5)]
task = [test(5, i+1) for i in roll_list]
# 创建事件循环,并把把task列表里的任务加到时间循环里执行(asycio.wait()是随机选取一个任务执行的)
asyncio.run(asyncio.wait(task))
在某些第三方模块不支持异步操作时,可以使用异步+线程池/进程池的方式
import asyncio
import concurrent.futures
import time
# 模拟不支持异步的操作
def fun(value):
time.sleep(1)
print(value)
return True
# 异步 + 多线程池
async def main(value):
loop = asyncio.get_running_loop()
fut = loop.run_in_executor(None, fun, value)
result = await fut
list1 = [i**2 for i in range(10)]
task = [main(i) for i in list1]
asyncio.run(asyncio.wait(task))
总结
简单介绍了下Python协程与异步编程