在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 Tkinter基础控件用法
Sep 03 Python
跟老齐学Python之正规地说一句话
Sep 28 Python
用python实现面向对像的ASP程序实例
Nov 10 Python
浅谈python中set使用
Jun 30 Python
Python字典操作详细介绍及字典内建方法分享
Jan 04 Python
Python中str.join()简单用法示例
Mar 20 Python
Python 编程速成(推荐)
Apr 15 Python
Python 中的参数传递、返回值、浅拷贝、深拷贝
Jun 25 Python
Numpy将二维数组添加到空数组的实现
Dec 05 Python
python GUI库图形界面开发之PyQt5输入对话框QInputDialog详细使用方法与实例
Feb 27 Python
Python 去除字符串中指定字符串
Mar 05 Python
pyspark对Mysql数据库进行读写的实现
Dec 30 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的压缩函数实现:gzencode、gzdeflate和gzcompress的区别
2016/01/27 PHP
利用php-cli和任务计划实现刷新token功能的方法
2017/05/03 PHP
PHP根据树的前序遍历和中序遍历构造树并输出后序遍历的方法
2017/11/10 PHP
php使用curl模拟多线程实现批处理功能示例
2019/07/25 PHP
javascript面向对象编程代码
2011/12/19 Javascript
jquery 关于event.target使用的几点说明介绍
2013/04/26 Javascript
js获取当月最后一天实例代码
2013/11/19 Javascript
JavaScript实现时间倒计时跳转(推荐)
2016/06/28 Javascript
JS中SetTimeout和SetInterval使用初探
2017/03/23 Javascript
理解 Node.js 事件驱动机制的原理
2017/08/16 Javascript
Node做中转服务器转发接口
2017/10/18 Javascript
详解给Vue2路由导航钩子和axios拦截器做个封装
2018/04/10 Javascript
vue-cli项目中使用Mockjs详解
2018/05/14 Javascript
用JS实现选项卡
2020/03/23 Javascript
如何解决vue在ios微信"复制链接"功能问题
2020/03/26 Javascript
JavaScript中数组去重的5种方法
2020/07/04 Javascript
openlayers4.6.5实现距离量测和面积量测
2020/09/25 Javascript
[04:31]2016国际邀请赛中国区预选赛妖精采访
2016/06/27 DOTA
Python import自定义模块方法
2015/02/12 Python
python实现复制整个目录的方法
2015/05/12 Python
numpy中的高维数组转置实例
2018/04/17 Python
Python3 读、写Excel文件的操作方法
2018/10/20 Python
Python3.5 Pandas模块之DataFrame用法实例分析
2019/04/23 Python
python查看数据类型的方法
2019/10/12 Python
Python爬虫headers处理及网络超时问题解决方案
2020/06/19 Python
Cotton On南非:澳洲时尚平价品牌
2018/06/28 全球购物
几道Web/Ajax的面试题
2016/11/05 面试题
学期自我鉴定
2013/11/04 职场文书
2014爱耳日宣传教育活动总结
2014/03/09 职场文书
十佳护士先进事迹
2014/05/08 职场文书
质量管理标语
2014/06/12 职场文书
区政府领导班子个人对照检查材料
2014/09/25 职场文书
家装业务员岗位职责
2015/04/03 职场文书
《没有任何借口》读后感:完美的执行能力
2020/01/07 职场文书
MyBatis自定义SQL拦截器示例详解
2021/10/24 Java/Android
HTML静态页面获取url参数和UserAgent的实现
2022/08/05 HTML / CSS