为什么asyncio不总是使用执行程序?

香港

我必须发送很多HTTP请求,一旦它们全部返回,程序就可以继续。听起来像是完美的搭配asyncio天真地,我将to的调用包装requests在一个async函数中,然后将它们交给asyncio这行不通。

在网上搜索后,我发现了两种解决方案:

  • 使用像aiohttp这样的库,该可以与asyncio
  • 将阻塞代码包装为 run_in_executor

为了更好地理解这一点,我编写了一个小型基准。服务器端是一个Flask程序,它在响应请求之前等待0.1秒。

from flask import Flask
import time

app = Flask(__name__)


@app.route('/')
def hello_world():
    time.sleep(0.1) // heavy calculations here :)
    return 'Hello World!'


if __name__ == '__main__':
    app.run()

客户是我的基准

import requests
from time import perf_counter, sleep

# this is the baseline, sequential calls to requests.get
start = perf_counter()
for i in range(10):
    r = requests.get("http://127.0.0.1:5000/")
stop = perf_counter()
print(f"synchronous took {stop-start} seconds") # 1.062 secs

# now the naive asyncio version
import asyncio
loop = asyncio.get_event_loop()

async def get_response():
    r = requests.get("http://127.0.0.1:5000/")

start = perf_counter()
loop.run_until_complete(asyncio.gather(*[get_response() for i in range(10)]))
stop = perf_counter()
print(f"asynchronous took {stop-start} seconds") # 1.049 secs

# the fast asyncio version
start = perf_counter()
loop.run_until_complete(asyncio.gather(
    *[loop.run_in_executor(None, requests.get, 'http://127.0.0.1:5000/') for i in range(10)]))
stop = perf_counter()
print(f"asynchronous (executor) took {stop-start} seconds") # 0.122 secs

#finally, aiohttp
import aiohttp

async def get_response(session):
    async with session.get("http://127.0.0.1:5000/") as response:
        return await response.text()

async def main():
    async with aiohttp.ClientSession() as session:
        await get_response(session)

start = perf_counter()
loop.run_until_complete(asyncio.gather(*[main() for i in range(10)]))
stop = perf_counter()
print(f"aiohttp took {stop-start} seconds") # 0.121 secs

因此,具有的直观实现asyncio无需处理阻塞io代码。但是,如果使用asyncio正确,它与特殊aiohttp框架一样快协程和任务的文档并没有真正提到这一点。仅当您阅读loop.run_in_executor()时,它才会说:

# File operations (such as logging) can block the
# event loop: run them in a thread pool.

我对此行为感到惊讶。异步的目的是加快阻塞io调用。为什么需要一个额外的包装器run_in_executor来做到这一点?

的整个卖点aiohttp似乎是对的支持asyncio但据我所知,requests只要您将其包装在执行程序中,该模块就可以完美运行。是否有理由避免将某些东西包装在执行器中?

米哈伊尔·杰拉西莫夫(Mikhail Gerasimov)

但据我所知,requests模块可以完美地工作-只要将其包装在执行程序中即可。是否有理由避免将某些东西包装在执行器中?

在executor中运行代码意味着要在OS线程中运行它

aiohttp 和类似的库允许仅使用协程在没有OS线程的情况下运行非阻塞代码。

如果您没有太多工作,则OS线程和协程之间的差异并不明显,尤其是与瓶颈-I / O操作相比。但是,一旦您做了很多工作,您会注意到由于上下文切换成本高昂,OS线程的性能相对较差

例如,当我将代码更改为time.sleep(0.001)和时range(100),我的机器将显示:

asynchronous (executor) took 0.21461606299999997 seconds
aiohttp took 0.12484742700000007 seconds

而且这种差异只会根据请求数而增加。

异步的目的是加快阻塞io调用。

否,目的asyncio是提供一种方便的方式来控制执行流程。asyncio允许您根据协程和OS线程(使用执行程序时)或基于纯粹的协程(像aiohttp这样)来选择流的工作方式

aiohttp的目的是加快东西,并将它与任务科佩斯如上图所示:)

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

为什么在执行程序时使用“ ./”?

蜂巢火花-为什么不'select *'产生火花应用程序/执行程序?

使用ProcessPoolExecutor在执行程序中运行AsyncIO

为什么“ ./a.out”仅运行最后一个编译的可执行程序,而不运行先前的可执行程序?

RxJava-为什么执行程序仅使用一个线程

为什么没有使用所有可用执行程序的Spark大舞台?

为什么我的执行程序的内存使用量停留在0?

为什么Promise的执行程序函数arg名为resolve()?

取消BackgroundWorker后为什么要执行程序?

为什么不总是使用缓冲流?

为什么不总是使用std :: forward?

为什么不总是使用 EnableCrossPartitionQuery

为什么不总是使用断言?

为什么将OpenCL内核排入队列以执行程序会导致程序的内存使用量增加?

使用Java执行程序的正确方法是什么?

为什么会收到“已使用非定义参数调用了Promise执行程序”警告?(Node.js)

使用“ Runas”执行程序

使用crontab执行程序

使用休眠执行程序

使用执行程序运行 asyncio 会引发我的返回类型不可调用的错误

什么时候需要使用执行程序来执行异步代码?当我不这样做(但应该这样做)时会发生什么?

为什么当我尝试以普通用户身份执行程序时却被拒绝,而当我使用sudo执行该程序时却得到“未找到”

Apache Spark-为什么要删除执行程序?“空闲”是什么意思?

asyncio:从执行程序中的异步函数收集结果

为什么不总是使用enctype =“ multipart / form-data”?

为什么不总是使用对象参数列表

为什么不总是使用抽象而不是接口?

为什么不总是使用android:configChanges =“ keyboardHidden | orientation”?

为什么不总是使用fpic(位置独立代码)?