在Python 的线程中运行协程的方法
作者:kingname 发布时间:2021-03-10 18:06:38
在一篇文章 理解Python异步编程的基本原理 这篇文章中,我们讲到,如果在异步代码里面又包含了一段非常耗时的同步代码,异步代码就会被卡住。
那么有没有办法让同步代码与异步代码看起来也是同时运行的呢?方法就是使用事件循环的.run_in_executor()方法。
我们来看一下 Python 官方文档[1]中的说法:
那么怎么使用呢?还是以非常耗时的递归方式计算斐波那契数列的这个函数为例:
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秒钟:
我们再来看看如果直接把这计算斐波那契数列和请求网站的两个异步任务放在一起“并行”,实际时间是两个任务的时间叠加:
具体原因我在上一篇文章里面已经做了说明。
现在,我想让两个任务“同时运行”,于是就可以这样修改代码:
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())
运行效果如下图所示:
在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个阻塞式的同步函数“并行”。
来源:https://developer.51cto.com/art/202002/611135.htm


猜你喜欢
- 在数据库查询的时候,我们有时有这样的需求,就是要找出数据表里指定范围行内的数据记录,比如说要找出数据表里第10行到第20行的这10条数据,那
- 使用MySql的窗口函数统计数据时,发现一个小的问题,与大家一起探讨下。环境配置:mysql-installer-community-8.0
- 基于Vue的页面切换左右滑动效果,具体内容如下HTML文本页面:<template> <div id="app&
- python2:print语句,语句就意味着可以直接跟要打印的东西,如果后面接的是一个元组对象,直接打印python3:print函数,函数
- js:controller:
- 前言一般而言,新的 centos 7.x 中自带的 python 都是 2.x 的版本。对于我们运行 python 软件支持并不友好,所以需
- 分组:根据研究目的,将所有样本点按照一个或多个属性划分为多个组,就是分组。pandas中,数据表就是DataFrame对象,分组就是grou
- 一 前言前一段时间接二连三的出现开发人员在测试环境和生产误操作导致数据库误删除/更新,对DBA而言,回滚数据着实是一件头疼的事情,凡涉及到恢
- 代码伺候:先看如下代码:例1: message = Message.objects.filter(pk=message_id2)messag
- python在安装的时候,就自带了很多模块,我们把这些模块称之为标准库,其中,有一个是使用频率比较高的,就是 os 。这个库中方法和属性众多
- 情况1: father.php如下定义: <?php $jack = 1000; ?> children.php 如下定义: &
- 在linux安装mysql是一个困难的事情,yum安装一般是安装的mysql5.1,现在经过自己不懈努力终于能用yum安装mysql5.5了
- 本文介绍使用aspjpeg组件实现图片的半透明描边的效果,描边效果演示:参数说明'big 原图路径(相对)'small 生成
- 我们之前看到了函数和对象。从本质上来说,它们都是为了更好的组织已经有的程序,以方便重复利用。模块(module)也是为了同样的目的。在Pyt
- int(x, [base])功能:函数的作用是将一个数字或base类型的字符串转换成整数。函数原型:int(x=0)int(x, base=
- 我自己测试一下,很多字符变成了 ‘?'。数据库连接已经是使用了 utf8 字符集:define("MYSQL_ENCODE
- 一、数据库的建立和销毁建立数据库:create database [if not exists] 数据库名 [default charset
- 本文介绍的实例成功的实现了动态行转列。下面我以一个简单的数据库为例子,说明一下。数据表结构这里我用一个比较简单的例子来说明,也是行转列的经典
- 网站标准(或称“WEB标准”)对于每一个开发网站和做网页的人来说,都是不可忽视的,这不仅是一个潮流,而是一个标准,一个更加符合规范的做法,而
- 1、问题现象:有个网站是通过session验证的,前端时间访问正常,但近期后台session老是失效很快,根本没法操作,我设置timeout