Запланированные задачи в sure_future перекрываются последними - PullRequest
0 голосов
/ 03 октября 2018

Мне нужен новый обработчик, который использует другой существующий обработчик для обработки нескольких запросов одновременно:

async def process_multiple_queries(request):
    init_request = request.clone()
    body = await request.json()

    for query in body["queries"]:

        @asyncio.coroutine
        def fake_json(self):
            return query

        # Replace request body on new fake json
        R = init_request.clone()
        R.json = fake_json.__get__(R, Request)

        # Process request in the background task
        asyncio.ensure_future(process_query(R))

async def process_query(request):
    body = await request.json()
    # ... Process body ...
    return web.json_response({"result": "OK"})

Однако результаты всех запланированных задач равны результату последней задачи, то есть телам всехзапросы, переданные в process_query, равны только телу последнего запроса.

Но если я добавлю asyncio.sleep(5) после asyncio.ensure_future(process_query(R)), все задачи будут обработаны правильно и приведут к разным результатам.

Как это исправить?

1 Ответ

0 голосов
/ 03 октября 2018

Это широко известный Python-метод, в котором замыкание захватывает переменные по имени, а не по значению.В результате все экземпляры сопрограммы fake_json ссылаются на одну и ту же query переменную , поэтому все в конечном итоге ссылаются на ее последнее известное значение.

Чтобы решить эту проблему, необходимо изменить определение:

        @asyncio.coroutine
        def fake_json(self):
            return query

на формулировку, которая фиксирует значение query, например:

        @asyncio.coroutine
        def fake_json(self, query=query):
            return query

await asyncio.sleep(5) помогает, потому чтоон позволяет process_query завершиться до того, как значение query изменится из-под ног.Вы получите тот же эффект с await process_query(R).

Две точки, не связанные с вопросом:

  • Декоратор @asyncio.coroutine устарел и нет причин использовать его в новом коде (если только вы не нацелены на совместимость с Python до 3.5).Вместо этого просто определите fake_json, используя async def.

  • Вам не нужно звонить __get__, чтобы связать self с fake_json, что даже неиспользуй это.Вы можете определить fake_json, который не self, и просто назначить R.json = fake_json.

...