Загрузка данных


import asyncio
import random


WORKERS_COUNT = 10


async def process_url(url: str) -> str:
    sleep = round(random.random(), 2)
    print("\tStarted", url)
    await asyncio.sleep(sleep)
    print(f"\tFinished by {sleep} seconds", url)
    return url


async def main():
    # Эксперимент действительно работает корректно, но это все еще достаточно
    # "головоломное" решение
    # Очень легко при каком-то рефакторинге или добавлении функционала разломать это,
    # особенно, если станет код чуть более похож на реалистичный, а не для эксперимента
    # Правильно при подобном подходе с батчевыми воркерами все-же использовать
    # asyncio.Queue()

    # Александр, еще раз спасибо за интересный пример! :)

    urls = [f"url{i}" for i in range(1, 21)]

    result = {}
    iter_var = 0

    async def worker(worker_id: int):
        nonlocal iter_var

        while True:
            try:
                print("Worker", worker_id, f"{iter_var=}")
                url = urls[iter_var]
                # Даже перенос инкремента на после await не ломает слишком сильно,
                # только лишние вызовы в конце появляются
                iter_var += 1
                result[url] = await process_url(url)
            except IndexError:
                return

    await asyncio.gather(*([worker(i) for i in range(WORKERS_COUNT)]))

    # CHECK (не должен ничего вывести)
    urls_check_set = set()
    for k, v in result.items():
        if k != v:
            print("URL != URL")
        if k not in urls_check_set:
            urls_check_set.add(k)
    if len(urls_check_set) != len(set(urls)):
        print(len(urls_check_set))
    if urls_check_set != set(urls):
        print(urls_check_set.symmetric_difference(set(urls)))


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


    # CHECK (should not print)
    urls_check_set = set()
    for k, v in result.items():
        if k != v:
            print("URL != URL")
        if k not in urls_check_set:
            urls_check_set.add(k)
    if len(urls_check_set) != len(set(urls)):
        print(len(urls_check_set))
    if urls_check_set != set(urls):
        print(urls_check_set.symmetric_difference(set(urls)))


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