Как легко найти сопрограмму, время которой истекло? - PullRequest
1 голос
/ 14 мая 2019

ключевая проблема: asyncio.wait(aws,timeout=1,return_when=FIRST_COMPLETED) Существует ли простой способ проверить, истекло ли время для возвращенной задачи?

Это расширенный вопрос.

Сцена такая:

  • Общее количество сопрограмм неизвестно
  • сервер допускает только 10 ссылок
  • Сервер вернет, казалось бы, правильный результат (например, вернет неправильную страницу)
  • Сервер иногда не возвращает никаких данных.
  • Максимально возможный доступ ко всем данным

Итак, чтобы получить данные быстрее, мне нужно ограничить количество сопрограмм. Проверьте возвращенную страницу. И тайм-аут.

В настоящее время существует два простых метода.
1. аналогично потоку, используйте очередь для создания пула сопрограмм + 10 бесконечных циклов коро. Мне это не очень нравится. На самом деле этот метод работает очень быстро.
2. Я пытался использовать высокоуровневый API async python3.7, попытаться упростить структуру программы, используя while tasks & asyncio.wait & return_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 Ответ

1 голос
/ 15 мая 2019

Проблема в том, что подход с использованием 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.Эта проверка должна выполняться только среди задач done .

...