【问题标题】:How to make request without blocking (using asyncio)?如何在不阻塞的情况下发出请求(使用 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 让它变得如此困难。

标签: python python-3.x python-requests python-asyncio


【解决方案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.stackoverflow.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)或在另一个线程中运行整个事件循环。
猜你喜欢
  • 1970-01-01
  • 2018-12-13
  • 1970-01-01
  • 2011-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-26
  • 1970-01-01
相关资源
最近更新 更多