Я пытался воспроизвести и лучше понять пример TaskPool в этом блоге Кристиана Гарсиа, и я столкнулся с очень интересным результатом.
Вот два сценария, которые яиспользуемый.Я заменил фактический сетевой запрос случайным сном
#task_pool.py
import asyncio
class TaskPool(object):
def __init__(self, workers):
self._semaphore = asyncio.Semaphore(workers)
self._tasks = set()
async def put(self, coro):
await self._semaphore.acquire()
task = asyncio.create_task(coro)
self._tasks.add(task)
task.add_done_callback(self._on_task_done)
def _on_task_done(self, task):
self._tasks.remove(task)
self._semaphore.release()
async def join(self):
await asyncio.gather(*self._tasks)
async def __aenter__(self):
return self
def __aexit__(self, exc_type, exc, tb):
print("aexit triggered")
return self.join()
И
# main.py
import asyncio
import sys
from task_pool import TaskPool
import random
limit = 3
async def fetch(i):
timereq = random.randrange(5)
print("request: {} start, delay: {}".format(i, timereq))
await asyncio.sleep(timereq)
print("request: {} end".format(i))
return (timereq,i)
async def _main(total_requests):
async with TaskPool(limit) as tasks:
for i in range(total_requests):
await tasks.put(fetch(i))
loop = asyncio.get_event_loop()
loop.run_until_complete(_main(int(sys.argv[1])))
Команда main.py 10
на Python 3.7.1 дает следующий результат.
request: 0 start, delay: 3
request: 1 start, delay: 3
request: 2 start, delay: 3
request: 0 end
request: 1 end
request: 2 end
request: 3 start, delay: 4
request: 4 start, delay: 1
request: 5 start, delay: 0
request: 5 end
request: 6 start, delay: 1
request: 4 end
request: 6 end
request: 7 start, delay: 1
request: 8 start, delay: 4
request: 7 end
aexit triggered
request: 9 start, delay: 1
request: 9 end
request: 3 end
request: 8 end
У меня есть несколько вопросов, основанных на этом результате.
- Я бы не ожидал, что задачи будут выполняться, пока менеджер контекста не выйдет и не сработает
__aexit__
, потому что это единственный триггердля asyncio.gather
.Однако заявления о печати настоятельно предполагают, что задания fetch
выполняются еще до aexit
.Что именно происходит?Работают ли задачи?Если так, то с чего они начались? - Относится к (1).Почему диспетчер контекста завершает работу до того, как все задания возвращаются?
- Задание
fetch
должно возвращать кортеж.Как я могу получить доступ к этому значению?Я полагаю, что для веб-приложения разработчик может захотеть выполнить операции с данными, возвращаемыми веб-сайтом.
Любая помощь приветствуется!