优雅简单玩转python3异步并发 在python3之后,随着async/await引入,异步调用以全新而便捷的方式让人眼前一亮。 ## 首先,尽量用async/await定义协程 这里以使用aiohttp请求网络,async函数中,不要使用blocking io(比如requests,传统的mysql/redis库),否则仍然会阻塞全局。 下面的例子展示如何使用协程方式写一个http get请求 ```python async def get_http(url): async with aiohttp.ClientSession() as session: async with session.get(url) as resp: print("开始运行", url) js_str = await resp.text() print(len(js_str)) ``` ## 调用协程 1. 调用协程并不会立刻得到返回的结果,如果想等待future/task的结果,使用await 2. 如果想并发执行,并统一处理结果,使用loop.create_task,在创建任务的时候,任务就会执行 3. task可await, 可先把创建的task保存,再统一await 下面的示例,将以并发调用5次baidu。且只使用一个线程。 ```python async def get_https(): loop = asyncio.get_event_loop() tasks = [] for i in range(5): task = loop.create_task(get_http("http://www.baidu.com")) tasks.append(task) for task in tasks: await task ``` ## 最后,在main函数执行并等待全部任务完成 ```python async def run(): await get_https() loop = asyncio.get_event_loop() loop.run_until_complete(run()) ``` 用其它可异步的IO(一般和网络相关)替换现有的blocking io的库,如aiomysql aioredis 以及其它以aio开头的库 时至今日,异步io库都已经覆盖主流框架和需求。 ## 使用executor调用同步IO或cpu操作 这是一大创举,这个语法糖太香了。那就是使用`loop.run_in_executor`,让多线程操作与协程/任务模型无缝衔接起来。 这里举一个栗子,并发调用两次sleep ``` async def run(): start = time.time() loop = asyncio.get_event_loop() # 第一个参数传None,使用默认的线程池,一般为5个线程,一般够用 # 第二个参数是函数,而非函数的执行,这里与create_task不同,create_task传入的是 co(), 这里传入的是fn 没有括号执行。 f1 = loop.run_in_executor(None, lambda: time.sleep(1)) f2 = loop.run_in_executor(None, lambda: time.sleep(1)) await f1 # 仍然可以用await关键字等待结果 await f2 # 耗时1s,说明两个sleep是并发执行的 print(time.time()-start) ``` 有了asyncio和loop,向以前的并发(线程)使用方式说拜拜吧。:) 来自 大脸猪 写于 2022-12-04 19:26 -- 更新于2022-12-04 20:05 -- 0 条评论