在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 相关文章推荐
Nginx搭建HTTPS服务器和强制使用HTTPS访问的方法
Aug 16 Python
如何使用VSCode愉快的写Python于调试配置步骤
Apr 06 Python
利用python如何处理百万条数据(适用java新手)
Jun 06 Python
Python Series从0开始索引的方法
Nov 06 Python
Python格式化字符串f-string概览(小结)
Jun 18 Python
python中的TCP(传输控制协议)用法实例分析
Nov 15 Python
django数据模型on_delete, db_constraint的使用详解
Dec 24 Python
python3实现网页版raspberry pi(树莓派)小车控制
Feb 12 Python
Python读取JSON数据操作实例解析
May 18 Python
Python使用jupyter notebook查看ipynb文件过程解析
Jun 02 Python
Python用SSH连接到网络设备
Feb 18 Python
python 高阶函数简单介绍
Feb 19 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 has encountered an Access Violation 错误的解决方法
2010/01/17 PHP
for循环连续求和、九九乘法表代码
2012/02/20 PHP
使用php转义输出HTML到JavaScript
2015/03/27 PHP
firefox浏览器下javascript 拖动层效果与原理分析代码
2007/12/04 Javascript
javascript检测浏览器flash版本的实现代码
2011/12/06 Javascript
js三种排序算法分享
2012/08/16 Javascript
js获取本机的外网/广域网ip地址完整源码
2013/08/12 Javascript
js字符串截取函数substr substring slice使用对比
2013/11/27 Javascript
jquery分页插件jquery.pagination.js使用方法解析
2016/04/01 Javascript
Node.js下自定义错误类型详解
2016/10/17 Javascript
获取今天,昨天,本周,上周,本月,上月时间(实例分享)
2017/01/04 Javascript
AngularJS Controller作用域
2017/01/09 Javascript
Vue 2.X的状态管理vuex记录详解
2017/03/23 Javascript
Vue+SpringBoot开发V部落博客管理平台
2017/12/27 Javascript
jquery动态添加以及遍历option并获取特定样式名称的option方法
2018/01/29 jQuery
react-native 圆弧拖动进度条实现的示例代码
2018/04/12 Javascript
小程序实现列表点赞功能
2018/11/02 Javascript
微信小程序实现登录注册tab切换效果
2020/12/29 Javascript
vuex 动态注册方法 registerModule的实现
2019/07/03 Javascript
vue 将多个过滤器封装到一个文件中的代码详解
2020/09/05 Javascript
vue 数据双向绑定的实现方法
2021/03/04 Vue.js
[52:20]VP vs VG Supermajor小组赛 B组胜者组决赛 BO3 第一场 6.2
2018/06/03 DOTA
学习python (1)
2006/10/31 Python
横向对比分析Python解析XML的四种方式
2016/03/30 Python
Python 的AES加密与解密实现
2019/07/09 Python
Python谱减法语音降噪实例
2019/12/18 Python
Python生成词云的实现代码
2020/01/14 Python
CSS3实现文本垂直排列的方法
2018/07/10 HTML / CSS
实例讲解利用HTML5 Canvas API操作图形旋转的方法
2016/03/22 HTML / CSS
你对IPv6了解程度
2016/02/09 面试题
土木工程毕业生自荐信
2013/11/12 职场文书
初中班主任寄语
2014/04/04 职场文书
预备党员综合考察材料
2014/05/31 职场文书
纪律委员竞选稿
2015/11/19 职场文书
志愿者服务宣传标语口号
2015/12/26 职场文书
原生Js 实现的简单无缝滚动轮播图的示例代码
2021/05/10 Javascript