如何轻松找到超时的协程?

时间:2019-05-14 18:05:38

标签: python python-asyncio

关键问题:asyncio.wait(aws,timeout=1,return_when=FIRST_COMPLETED)是否有简单的方法来检查返回的任务是否超时?

这是一个扩展的问题。

场景是这样的:

  • 协程的总数未知
  • 服务器仅允许10个链接
  • 服务器将返回看似正确的结果(例如,返回错误的页面)
  • 服务器有时不返回任何数据。
  • 最大可能访问所有数据

因此,为了更快地获取数据,我需要限制协程的数量。检查返回的页面。还有超时。

目前有两种简单的方法。
1.与线程类似,使用队列构建协程池+ 10个无限循环coro。我不太喜欢实际上,这种方法非常快速。
2.我尝试使用异步python3.7的高级API,尝试使用while tasksasyncio.waitreturn_when简化程序的结构。

在这里,我遇到了如何找到协程超时的问题。

我建立了一个简单的演示:

import asyncio


async def test(delaytime):
    print(f"begin {delaytime}")
    await asyncio.sleep(delaytime )
    print(f"finish {delaytime} ")

async def main():
    # the number of tasks is unknow,range(10) is just a demo
    allts = list(range(10))
    ts = []
    while len(ts)<5:
        arg = allts.pop()
        t = asyncio.create_task(test(arg))
        t.arg = arg
        ts.append(t)
    while ts:
        dones,pendings = await asyncio.wait(ts,timeout=2,return_when=asyncio.FIRST_COMPLETED)
        for t in dones:
            # if check t.result() is error , i can append ts again
            print(t.arg,"is done")
            ts.remove(t)
            while len(ts)<5:
                if len(allts):
                    arg = allts.pop()
                    t = asyncio.create_task(test(arg))
                    t.arg = arg
                    ts.append(t)
                else:
                    break
        # for t in pendings:
        #   # if can check t is timeout , i can append ts again
        #   pass

if __name__=="__main__":
    asyncio.run(main())

调试之后,我知道return_when=asyncio.FIRST_COMPLETED返回的任务asyncio.wait在待处理的任务中,除了已完成的任务。
但是,我无法确定哪个任务超时。 我曾考虑过使用wait_for,但是wait_for没有return_when参数。

是否有一种简单的方法来确定超时任务以便重新加入ts

1 个答案:

答案 0 :(得分:1)

问题在于,使用wait(return_when=FIRST_COMPLETED)的方法与timeout的使用根本不兼容。由于不同的任务在不同的时间开始执行,因此一个timeout参数显然不能应用于所有任务。如果要使用return_when=FIRST_COMPLETED,请将每个任务包装在asyncio.wait_for中:

t = asyncio.create_task(asyncio.wait_for(test(arg), 2))

然后,当任务完成时,您可以使用t.exception()测试它是否超时,在这种情况下它将返回asyncio.TimeoutError。此检查只能在完成任务中执行。