在Python 的线程中运行协程的方法


Posted in Python onFebruary 24, 2020

在一篇文章 理解Python异步编程的基本原理 这篇文章中,我们讲到,如果在异步代码里面又包含了一段非常耗时的同步代码,异步代码就会被卡住。

那么有没有办法让同步代码与异步代码看起来也是同时运行的呢?方法就是使用事件循环的.run_in_executor()方法。

我们来看一下 Python 官方文档[1]中的说法:

在Python 的线程中运行协程的方法

那么怎么使用呢?还是以非常耗时的递归方式计算斐波那契数列的这个函数为例:

def sync_calc_fib(n): 
 if n in [1, 2]: 
 return1 
 return sync_calc_fib(n - 1) + sync_calc_fib(n - 2) 
async def calc_fib(n): 
 result = sync_calc_fib(n) 
 print(f'第 {n} 项计算完成,结果是:{result}') 
 return result

我们现在需要用 aiohttp 访问一个延迟5秒的网页,同时计算斐波那契数列第36项。

首先我们看看单独计算第36项需要5秒钟:

在Python 的线程中运行协程的方法

我们再来看看如果直接把这计算斐波那契数列和请求网站的两个异步任务放在一起“并行”,实际时间是两个任务的时间叠加:

具体原因我在上一篇文章里面已经做了说明。

在Python 的线程中运行协程的方法

现在,我想让两个任务“同时运行”,于是就可以这样修改代码:

import aiohttp 
import asyncio 
import time 
from concurrent.futures import ThreadPoolExecutor 
async def request(sleep_time): 
 async with aiohttp.ClientSession() as client: 
 resp = await client.get(f'http://127.0.0.1:8000/sleep/{sleep_time}') 
 resp_json = await resp.json() 
 print(resp_json) 
def sync_calc_fib(n): 
 if n in [1, 2]: 
 return 1 
 return sync_calc_fib(n - 1) + sync_calc_fib(n - 2) 
def calc_fib(n): 
 result = sync_calc_fib(n) 
 print(f'第 {n} 项计算完成,结果是:{result}') 
 return result 
async def main(): 
 start = time.perf_counter() 
 loop = asyncio.get_event_loop() 
 with ThreadPoolExecutor(max_workers=4) as executor: 
 tasks_list = [ 
  loop.run_in_executor(executor, calc_fib, 36), 
  asyncio.create_task(request(5)) 
 ] 
 await asyncio.gather(*tasks_list) 
 end = time.perf_counter() 
 print(f'总计耗时:{end - start}') 
asyncio.run(main())

运行效果如下图所示:

在Python 的线程中运行协程的方法

在5秒钟的时间,就把计算斐波那契数列和请求5秒延迟的网站都做完了。

实现这样的转变,关键的代码就是:loop.run_in_executor(executor, calc_fib, 36)

其中的 loop就是主线程的事件循环(event loop),它是用来调度同一个线程里面的多个协程。

executor是我们使用ThreadPoolExecutor(max_workers=4)创建的一个有4个线程的线程池,calc_fib是一个耗时的同步函数,36是传入calc_fib的参数。loop.run_in_executor(executor, calc_fib, 36)的意思是说:

  • 把calc_fib函数放到线程池里面去运行
  • 给线程池增加一个回调函数,这个回调函数会在运行结束后的下一次事件循环把结果保存下来。

请注意上图中红色箭头对应的calc_fib这是一个同步函数,请与上一篇文章中的异步函数区分开。run_in_executor的第二个参数需要是一个同步函数的函数名。

在上面的例子中,我们创建的是有4个线程的线程池。所以这个线程池最多允许4个阻塞式的同步函数“并行”。

总结

到此这篇关于在Python 的线程中运行协程的方法的文章就介绍到这了,更多相关python线程中运行协程内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python模糊查询本地文件夹去除文件后缀的实例(7行代码)
Nov 09 Python
python实现俄罗斯方块游戏
Mar 25 Python
解决python "No module named pip" 的问题
Oct 13 Python
连接pandas以及数组转pandas的方法
Jun 28 Python
简单了解django缓存方式及配置
Jul 19 Python
python检测服务器端口代码实例
Aug 31 Python
python3多线程知识点总结
Sep 26 Python
Django1.11自带分页器paginator的使用方法
Oct 31 Python
Python SQLAlchemy入门教程(基本用法)
Nov 11 Python
python读取ini配置文件过程示范
Dec 23 Python
TFRecord文件查看包含的所有Features代码
Feb 17 Python
python爬虫开发之urllib模块详细使用方法与实例全解
Mar 09 Python
Python 爬取必应壁纸的实例讲解
Feb 24 #Python
Python unittest工作原理和使用过程解析
Feb 24 #Python
Python 剪绳子的多种思路实现(动态规划和贪心)
Feb 24 #Python
用python介绍4种常用的单链表翻转的方法小结
Feb 24 #Python
关于多元线性回归分析——Python&SPSS
Feb 24 #Python
使用 pytorch 创建神经网络拟合sin函数的实现
Feb 24 #Python
sklearn+python:线性回归案例
Feb 24 #Python
You might like
php在程序中将网页生成word文档并提供下载的代码
2012/10/09 PHP
php创建session的方法实例详解
2015/01/27 PHP
php实现Session存储到Redis
2015/11/11 PHP
php双层循环(九九乘法表)
2017/10/23 PHP
关于flash遮盖div浮动层的解决方法
2010/07/17 Javascript
JavaScript日历实现代码
2010/09/12 Javascript
Prototype源码浅析 Enumerable部分(二)
2012/01/18 Javascript
jquery 实现窗口的最大化不论什么情况
2013/09/03 Javascript
js抽奖实现随机抽奖代码效果
2013/12/02 Javascript
JavaScript截取字符串的Slice、Substring、Substr函数详解和比较
2014/03/20 Javascript
Vue.js每天必学之指令系统与自定义指令
2016/09/07 Javascript
BootStrap学习系列之布局组件(下拉,按钮组[toolbar],上拉)
2017/01/03 Javascript
web.js.字符串与正则表达式操作
2017/05/13 Javascript
nodeJS实现简单网页爬虫功能的实例(分享)
2017/06/08 NodeJs
webpack进阶——缓存与独立打包的用法
2017/08/02 Javascript
vue移动端路由切换实例分析
2018/05/14 Javascript
微信小程序日期选择器实例代码
2018/07/18 Javascript
详解如何在nuxt中添加proxyTable代理
2018/08/10 Javascript
使用Vue调取接口,并渲染数据的示例代码
2019/10/28 Javascript
python的re模块应用实例
2014/09/26 Python
Python安装第三方库的3种方法
2015/06/21 Python
python3解析库pyquery的深入讲解
2018/06/26 Python
pytorch 批次遍历数据集打印数据的例子
2019/12/30 Python
如何在Win10系统使用Python3连接Hive
2020/10/15 Python
Html5新特性用canvas标签画多条直线附效果截图
2014/06/30 HTML / CSS
欧缇丽英国官方网站:Caudalie英国
2016/08/17 全球购物
阿玛尼意大利官网:Armani意大利
2018/10/30 全球购物
党员活动日总结
2014/05/05 职场文书
投标人廉洁自律承诺书
2014/05/26 职场文书
司法局群众路线教育实践活动开展情况总结
2014/10/25 职场文书
教师先进事迹材料
2014/12/16 职场文书
明星邀请函
2015/02/02 职场文书
教师节祝酒词
2015/08/11 职场文书
pytorch中Schedule与warmup_steps的用法说明
2021/05/24 Python
关于Spring配置文件加载方式变化引发的异常详解
2022/01/18 Java/Android
HTML静态页面获取url参数和UserAgent的实现
2022/08/05 HTML / CSS