【问题标题】:asyncio run_until_complete does not wait that all coroutines finishasyncio run_until_complete 不等待所有协程完成
【发布时间】:2021-01-31 09:49:07
【问题描述】:

我正在迈出 Python 的第一步,但我有点难以理解为什么我在这方面没有得到预期的结果。这是我想要实现的目标:

我有一个使用 API 的函数。在等待 API 回答并考虑到我正在通过一个会产生额外延迟的代理时,我认为发送并发请求会加快进程(我运行 100 个并发请求)。确实如此。但是 asyncio run_until_complete 总是返回一些未完成的协程。

这里是代码(简化):

import aiohttp
import asyncio
    
async def consume_api(parameter):
    url = "someurl" #it is actually based on the parameter
    try:
        async with aiohttp.ClientSession() as session:
            async with session.get(URL, proxy="someproxy") as asyncresponse:
                r = await asyncresponse.read()
    except:
        global error_count 
        error_count += 1
        if error_count > 50:
            return "Exceeded 50 try on same request"
        else:
            return consume_api(parameter)
    return r.decode("utf-8") 

def loop_on_api(list_of_parameter):
    loop = asyncio.get_event_loop()

    coroutines = [consume_api(list_of_parameter[i]) for i in range(len(list_of_parameter))]
    results = loop.run_until_complete(asyncio.gather(*coroutines))
    return results

当我运行调试器时,loop_on_api 函数返回的results 包含一个字符串列表,对应于consume_api 的结果和<coroutine objects consume_api at 0x00...> 的一些出现。这些变量在 False 和 cr_Frame 处具有 cr_running 参数。 虽然如果我检查 coroutines 变量,我可以找到所有 100 个协程,但似乎没有一个有 cr_Frame。

知道我做错了什么吗?

我也在考虑我计算 50 错误的方式将被所有协程共享。

知道如何具体说明吗?

【问题讨论】:

    标签: python python-asyncio aiohttp


    【解决方案1】:

    这应该可行,您可以添加/更改/重构您想要的任何内容

    import aiohttp
    import asyncio
    
    
    async def consume_api(url):
        async with aiohttp.ClientSession() as session:
            async with session.get(url) as response:
                return await response.read()
    
    
    def loop_on_api(list_of_urls):
        loop = asyncio.get_event_loop()
    
        coroutines = [consume_api(url) for url in list_of_urls]
    
        results = loop.run_until_complete(asyncio.gather(*coroutines))
        return results
    
    
    if __name__ == '__main__':
        print(loop_on_api(['https://google.com', 'https://twitter.com']))
    

    【讨论】:

    • 我看不出我的代码有什么不同,除了你删除了我在consume_api 中的错误管理(我必须保留)并且表达了稍微不同的如何获取参数(你重命名了 url) ...但这是完全相同的顺序。能否详细说明逻辑更新在哪里?
    • 让我们一步一步开始吧。这段代码对你有用吗?如果是这样,请尝试逐步添加您的额外内容,并且每次都运行该程序。
    • 没有步骤,你的代码和我的一模一样,所以请开发更改,因为对我来说整个事情是完全一样的
    • 您希望我将您的代码复制粘贴到问题中还是答案中?可以分享完整的代码吗?所以我可以复制它并运行它?你的参数列表是什么。
    • 仔细检查行为后,问题来自代理。因此,鉴于代理端不存在任何问题,代码将正常运行(您的和我的)。所以我无法避免不得不强制重新运行有故障的协程
    【解决方案2】:

    问题似乎来自我正在使用的代理,它有时不携带请求或响应。因此,强制重新运行似乎是最好的答案。因此我现在检查返回的结果是否还有一些协程,并在它们上重新运行loop_on_api()

    def loop_on_api(list_of_parameter):
        loop = asyncio.get_event_loop()
    
        coroutines = [consume_api(list_of_parameter[i]) for i in range(len(list_of_parameter))]
        results = loop.run_until_complete(asyncio.gather(*coroutines))
    
        undone = []
        rerun_list_of_parameter = []
        
        for i in range(len(results)):
            if str(type(results[i])) == "<class 'coroutine'>": #not very elegant >> is there a better way?
                undone.append(i)
                rerun_list_of_parameter.append(list_of_parameter[i])
    
        if len(undone) > 0:
            undone_results = loop_on_api(rerun_list_of_parameter)
            for i in range(len(undone_results)):
                results[undone[i]] = undone_results[i]
    
        return results
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-26
      • 2014-12-16
      • 2017-12-17
      • 1970-01-01
      • 2020-07-03
      相关资源
      最近更新 更多