Python3异步编程
Posted 点融黑帮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python3异步编程相关的知识,希望对你有一定的参考价值。
Python3.4之后引入asyncio标准库,并在3.5中提供原生语法支持,为编写异步程序提供了高效且优雅的方法。对于编写爬虫和httpserver的这类IO密集型应用,asyncio的表现非常亮眼。
asyncio基于协程实现,至于为什么不用进程or线程实现并发,忽略内核陷入开销以及GIL,进程与线程依赖操作系统调度,调度开销高,调度方式也不一定与应用适配。
协程是可以暂停和恢复的用户态“线程”。但协程的定义并不十分明确,也有多种实现。但总的来讲都是基于生成器,所以为了理解协程,先简单介绍一下python生成器:
1.栈帧PyFrameObject保存代码的信息和上下文
2.栈帧拥有自己的数据栈和block栈,解释器可以中断和恢复栈帧
3.python将函数编译成字节码时,碰到yield语句,标记它为生成器函数
4.程序调用生成器函数时,python创建生成器对象
5.所有调用特定生成器函数得到的生成器对象都指向同样的代码。但每个生成器对象都有独立的栈帧
6.调用send或next方法,由gen_send_ex函数执行系列操作、修改生成器状态,然后调用PyEval_EvalFrameEx执行字节码(如果代码块为空或调用栈为空,抛出StopIteration异常)
事件循环是asyncio的核心,它负责:
1.注册、执行以及取消超时调用
2.为各种通信创建client和server通道
3.启动子程序和并创建与外部程序通信的通道
4.将耗时任务委托给线程池
获取事件循环:
asyncio.get_event_loop_policy获取事件循环策略,asyncio.get_event_loop获取事件循环.
设置事件循环:
asyncio.set_event_loop_policy设置事件循环策略,asyncio.set_event_loop设置事件循环
以uvloop为例:
输出:
开启事件循环:
AbstractEventLoop.run_forever开启事件循环直到stop方法被调用
AbstractEventLoop.run_until_complete开启事件循环,如果参数是协程对象,ensure_future会将协程封装为Task。函数返回Futures的结果或者抛出相应的异常。
关闭事件循环:
AbstractEventLoop.stop如果run_forever正在运行,对当前批次执行callback后退出。
run_forever再次被调用时,继续执行。
AbstractEventLoop.close强制关闭事件循环,抛弃待处理的回调。并且关闭之后不可逆转。
封装callable的异步执行,Task的基类,非线程安全。
Task是Future的子类,负责在事件循环中执行协程。事件循环同一时间只执行一个task(其他线程中的任务可能可以“并行”)。当task等待其他future完成时,事件循环执行新的task(并发)。
使用asyncio.ensure_future函数或者AbstractEventLoop.create_task方法创建task,Task非线程安全。
对于非线程安全问题,一个event_loop只在一个线程中运行,所以只需要担心event_loop之外的情况,asyncio.run_coroutine_threadsafe与loop.call_soon_threadsafe可以应对。
接下来我们基于asyncio和aiohttp编写一个爬虫抓取豆瓣电影top250(隐去异常处理,下同):
COSTTIME: 0.25513544082641604S
线程池版本:
COSTTIME: 0.4078077793121338 S
这个对比虽不够严谨,但也可以看出asyncio的异步性能,特别是在并发量越来越大的时候,python可以轻松开启上万的协程,而开启上万线程操作系统可能会不堪重负。
上面我们介绍了协程和asyncio标准库,并编写了一个简单的网络爬虫,对比协程和多线程版本。实际上对于HTTPSERVER,Sanic+uvloop能创造出惊人的性能,详见uvloop:Blazing fast Python networking(
https://magic.io/blog/uvloop-blazing-fast-python-networking/)点击回顾往期精彩内容
以上是关于Python3异步编程的主要内容,如果未能解决你的问题,请参考以下文章