的收益未被用于未来

时间:2019-09-06 11:10:35

标签: python python-asyncio

我正在使用requests-threads运行以下命令:

def perform_requests():
    prepared_requests = [...]
    session = AsyncSession(n=100)
    results = []

    async def _perform_requests():
        for request in prepared_requests:
            results.append(session.request(**request))
        for i, result in enumerate(results):
            results[i] = await asyncio.ensure_future(results[i])

    session.run(_perform_requests)
    return results

但是,当我运行它时,会发生一些奇怪的事情,首先我会收到大量消息,例如:

(WARNING) Connection pool is full, discarding connection:

其次我得到这个错误:

results[i] = await asyncio.ensure_future(results[i])
builtins.AssertionError: yield from wasn't used with future

我正在使用ensure_future(),这是怎么回事?

1 个答案:

答案 0 :(得分:1)

session.request()方法返回一个扭曲的Deferred对象(requests-threads代码calls twisted.internet.threads.deferToThread())。通常,您不想将其视为异步任务,而不是在Twisted反应堆下运行。

相反,您可以使用twisted.internet.deferred.gatherResults()来同时执行请求并收集响应。

接下来,session.run()调用twisted.internet.task.react(),这将总是退出Python

  

[...]此功能还将:

     

[...]

     
      
  • 完成后退出应用程序,成功则退出代码为0,失败则退出代码。
  •   

(强调粗体的人)。

这意味着即使您的代码有效,也不会到达return results行。

如果将session.run()调用移出,成为应用程序的顶级入口点,那么事情就起作用了:

from requests_threads import AsyncSession
from twisted.internet import defer

session = AsyncSession(n=100)

async def perform_requests():
    prepared_requests = [...]
    requests = [session.request(**request) for request in prepared_requests]
    responses = await defer.gatherResults(requests)
    print(responses)        

session.run(perform_requests)

但在打印responses列表后立即退出。

否则,您将必须直接管理扭曲反应堆(使用reactor.run()和响应完成后调用reactor.stop()的回调);例如:

from requests_threads import AsyncSession
from twisted.internet import defer, error, reactor

def perform_requests():
    prepared_requests = [...]
    session = AsyncSession(n=100)
    results = []

    async def gather_responses():
        requests = [session.request(**request) for request in prepared_requests]
        results[:] = await defer.gatherResults(requests)
        try:
            reactor.stop()
        except error.ReactorNotRunning:
            pass

    deferred = defer.ensureDeferred(gather_responses())
    reactor.run()
    return results

print(perform_requests())

如果您需要在扭曲的反应堆上运行多个任务,则可以使用一个顶级函数,并依靠回调函数在响应完成时通知您。

我个人认为,使用aiohttp.client module在Python asyncio事件循环下运行异步请求会更好:

import asyncio
import aiohttp

async def perform_requests():
    prepared_requests = [...]
    conn = aiohttp.TCPConnector(limit=100)

    with aiohttp.ClientSession(connector=conn) as session:
        requests = [session.request(**request) for request in prepared_requests]
        responses = await asyncio.gather(*requests)

        print(responses)

if __name__ == '__main__':
    asyncio.run(perform_requests())

请注意,asyncio.run()需要Python 3.7或更高版本;您的错误消息表明您仍在使用3.5或3.6。 work-around would be to use loop.run_until_complete()

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(perform_requests())