首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

python教程:使用 async和await 协程进行并发编程

python 一直在进行并发编程的优化, 比较熟知的是使用 thread 模块多线程和 multiprocessing 多进程,后来慢慢引入基于 yield 关键字的协程。而近几个版本,python 对于协程的写法进行了大幅的优化,很多之前的协程写法不被官方推荐了。如果你之前了解过 python 协程,你应该看看最新的用法。

并发、并行、同步和异步

并发指的是一个 CPU 同时处理多个程序,但是在同一时间点只会处理其中一个。并发的核心是:程序切换。

但是因为程序切换的速度非常快,1 秒钟内可以完全很多次程序切换,肉眼无法感知。

并行指的是多个 CPU 同时处理多个程序,同一时间点可以处理多个。

同步:执行 IO 操作时,必须等待执行完成才得到返回结果。

异步:执行 IO 操作时,不必等待执行就能得到返回结果。

协程,线程和进程的区别

多进程通常利用的是多核 CPU 的优势,同时执行多个计算任务。每个进程有自己独立的内存管理,所以不同进程之间要进行数据通信比较麻烦。

多线程是在一个 cpu 上创建多个子任务,当某一个子任务休息的时候其他任务接着执行。多线程的控制是由 python 自己控制的。子线程之间的内存是共享的,并不需要额外的数据通信机制。但是线程存在数据同步问题,所以要有锁机制。

协程的实现是在一个线程内实现的,相当于流水线作业。由于线程切换的消耗比较大,所以对于并发编程,可以优先使用协程。

协程的基础使用

这是 python 3.7 里面的基础协程用法,现在这种用法已经基本稳定,不太建议使用之前的语法了。

import asyncioimport time

async def visit_url(url, response_time): """访问 url""" await asyncio.sleep(response_time) return f"访问{url}, 已得到返回结果"

start_time = time.perf_counter()task = visit_url('http://wangzhen.com', 2)asyncio.run(task)print(f"消耗时间:{time.perf_counter() - start_time}")

•1, 在普通的函数前面加 async 关键字;•2,await 表示在这个地方等待子函数执行完成,再往下执行。(在并发操作中,把程序控制权交给主程序,让他分配其他协程执行。) await 只能在带有 async 关键字的函数中运行。•3, asynico.run() 运行程序•4, 这个程序消耗时间 2s 左右。

增加协程

再添加一个任务:

task2 = visit_url('http://another.com', 3)asynicio.run(task2)

这 2 个程序一共消耗 5s 左右的时间。并没有发挥并发编程的优势。如果是并发编程,这个程序只需要消耗 3s,也就是task2的等待时间。要想使用并发编程形式,需要把上面的代码改一下。

import asyncioimport time

async def visit_url(url, response_time): """访问 url""" await asyncio.sleep(response_time) return f"访问{url}, 已得到返回结果"

async def run_task(): """收集子任务""" task = visit_url('http://wangzhen.com', 2) task_2 = visit_url('http://another', 3) await asyncio.run(task) await asyncio.run(task_2)

asyncio.run(run_task())print(f"消耗时间:{time.perf_counter() - start_time}")

asyncio.gather 会创建 2 个子任务,当出现 await 的时候,程序会在这 2 个子任务之间进行调度。

create_task

创建子任务除了可以用 gather 方法之外,还可以使用 asyncio.create_task 进行创建。

async def run_task(): coro = visit_url('http://wangzhen.com', 2) coro_2 = visit_url('http://another.com', 3)

task1 = asyncio.create_task(coro) task2 = asyncio.create_task(coro_2)

await task1 await task2

协程的主要使用场景

协程的主要应用场景是 IO 密集型任务,总结几个常见的使用场景:

•网络请求,比如爬虫,大量使用 aiohttp•文件读取, aiofile•web 框架, aiohttp, fastapi•数据库查询, asyncpg, databases

进一步学习方向(接下来的文章)

•什么时候用协程,什么时候用多线程,什么时候用多进程•future 对象•asyncio 的底层 api•loop•trio 第三方库用法

参考文献

•talkpython课程源码[1]•trio[2]•猿人学[3]•realpython[4]•Fear and Awaiting in Async by David Beazley[5]

References

[1]talkpython课程源码:https://github.com/talkpython/async-techniques-python-course

[2]trio:https://github.com/python-trio/trio

[3]猿人学:https://www.yuanrenxue.com/tricks/asyncio-example.html

[4]realpython:https://realpython.com/async-io-python/

[5]Fear and Awaiting in Async by David Beazley:https://www.youtube.com/watch?v=E-1Y4kSsAFc

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200420A0S0Y600?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券