如何在不阻塞的情况下发出请求(使用 asyncio)?
Posted
技术标签:
【中文标题】如何在不阻塞的情况下发出请求(使用 asyncio)?【英文标题】:How to make request without blocking (using asyncio)? 【发布时间】:2017-10-11 16:50:38 【问题描述】:我想使用asyncio
实现以下目标:
# Each iteration of this loop MUST last only 1 second
while True:
# Make an async request
sleep(1)
但是,我看到的唯一示例使用了一些变体
async def my_func():
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, requests.get, 'http://www.google.com')
loop = asyncio.get_event_loop()
loop.run_until_complete(my_func())
但是run_until_complete
正在阻止!在我的while
循环的每次迭代中使用run_until_complete
会导致循环阻塞。
过去几个小时我一直在试图弄清楚如何正确运行非阻塞任务(使用async def
定义)但没有成功。我一定遗漏了一些明显的东西,因为像这样简单的东西肯定应该很简单。我怎样才能实现我所描述的?
【问题讨论】:
循环本身总是阻塞的。理想情况下,您希望将您的 while 放在循环运行的协程中,但如果没有更好地了解您的代码应该如何工作,很难说更多。 你好,我发现自己正处于这种情况,我想要的只是正常的同步执行...(在一个while循环内)...with在需要时调用异步方法的能力。我不在乎结果。你能做到吗???请,谢谢 在 javascript 中这很容易实现,可惜 Python 让它变得如此困难。 【参考方案1】:run_until_complete
运行主事件循环。可以说它不是“阻塞”,它只是运行事件循环,直到您作为参数传递的协程返回。它必须挂起,否则程序要么停止,要么被下一条指令阻塞。
很难说你愿意实现什么,但这段代码确实做了一些事情:
async def my_func():
loop = asyncio.get_event_loop()
while True:
res = await loop.run_in_executor(None, requests.get, 'http://www.google.com')
print(res)
await asyncio.sleep(1)
loop = asyncio.get_event_loop()
loop.run_until_complete(my_func())
它将每秒钟在 Google 主页上执行一次 GET 请求,弹出一个新线程来执行每个请求。您可以通过虚拟并行运行多个请求来说服自己它实际上是非阻塞的:
async def entrypoint():
await asyncio.wait([
get('https://www.google.com'),
get('https://www.***.com'),
])
async def get(url):
loop = asyncio.get_event_loop()
while True:
res = await loop.run_in_executor(None, requests.get, url)
print(url, res)
await asyncio.sleep(1)
loop = asyncio.get_event_loop()
loop.run_until_complete(entrypoint())
要注意的另一件事是,您每次都在单独的线程中运行请求。它有效,但它有点像黑客。您应该使用真正的异步 HTTP 客户端,例如 aiohttp。
【讨论】:
谢谢,这让事情变得更清楚了,但这难道不是我的while
循环是异步的(当我不希望它是异步的时,如我的示例所示)?我想要的只是正常的同步执行,并且能够在需要时调用异步方法。我不在乎结果。使用线程这很简单 - Thread().start()
。也许使用asyncio
只需要我的心态发生重大变化。
asyncio 确实需要您更改应用程序的整个范例。想象一下你打电话给run_until_complete
,它没有挂起。在它之后,您可以调用time.sleep(5)
,它是一个阻塞函数。事实上,你之后调用的任何函数都不会给事件循环轮询的机会。协程永远不会执行,因为事件循环没有运行/轮询(记住:所有这一切都发生在一个线程上)。如果您稍后需要调用阻塞函数,请在另一个线程中执行(使用run_in_executor
)或在另一个线程中运行整个事件循环。以上是关于如何在不阻塞的情况下发出请求(使用 asyncio)?的主要内容,如果未能解决你的问题,请参考以下文章
如何让 Playframework 在不发出请求的情况下开始在开发模式下运行应用程序?
CORS 谷歌浏览器扩展有啥替代品吗?如何在不使用 CORS 的情况下成功发出 ajax 请求?