Подтвердить что ты не робот

Python 3.5 async/ожидание с примером реального кода

Я прочитал множество статей и учебник о Python 3.5 async/wait. Я должен сказать, что я довольно запутан, потому что некоторые используют get_event_loop() и run_until_complete(), некоторые используют make_future(), некоторые используют asyncio.wait(), а некоторые используют call_soon().

Похоже, у меня есть много вариантов, но я понятия не имею, полностью ли они идентичны или есть случаи, когда вы используете циклы, и есть случаи, когда вы используете wait().

Но все примеры работают с asyncio.sleep() как симуляция реальной медленной операции, которая возвращает ожидаемый объект. Как только я попытаюсь поменять эту строку на какой-то реальный код, все это не удастся. Какая разница между подходами, написанными выше, и как мне запускать стороннюю библиотеку, которая не готова к async/await. Я использую службу Quandl для получения данных о запасах.

 import asyncio
 import quandl

 async def slow_operation(n):
     # await asyncio.sleep(1) # Works because it await ready.
     await quandl.Dataset(n) # Doesn't work because it not await ready.


 async def main():
     await asyncio.wait([
         slow_operation("SIX/US9884981013EUR4"),
         slow_operation("SIX/US88160R1014EUR4"),
     ])

 # You don't have to use any code for 50 requests/day.
 quandl.ApiConfig.api_key = "MY_SECRET_CODE"

 loop = asyncio.get_event_loop()
 loop.run_until_complete(main())

Надеюсь, вы поняли, насколько я потерял себя, и насколько просто я хотел бы работать параллельно.

4b9b3361

Ответ 1

Если сторонняя библиотека не совместима с async/await, то, очевидно, вы не можете легко ее использовать. Есть два случая:

  • Скажем, что функция в библиотеке асинхронна и дает вам обратный вызов, например.

    def fn(..., clb):
        ...
    

    Итак, вы можете сделать:

    def on_result(...):
        ...
    
    fn(..., on_result)
    

    В этом случае вы можете обернуть такие функции в протокол asyncio следующим образом:

    from asyncio import Future
    
    def wrapper(...):
        future = Future()
        def my_clb(...):
            future.set_result(xyz)
        fn(..., my_clb)
        return future
    

    (используйте future.set_exception(exc) для исключения)

    Затем вы можете просто вызвать эту оболочку в некоторой функции async с помощью await:

    value = await wrapper(...)
    

    Обратите внимание, что await работает с любым объектом Future. Вам не нужно объявлять wrapper как async.

  • Если функция в библиотеке синхронна, вы можете запустить ее в отдельном потоке (возможно, для этого вам понадобится некоторый пул потоков). Весь код может выглядеть так:

    import asyncio
    import time
    from concurrent.futures import ThreadPoolExecutor
    
    # Initialize 10 threads
    THREAD_POOL = ThreadPoolExecutor(10)
    
    def synchronous_handler(param1, ...):
        # Do something synchronous
        time.sleep(2)
        return "foo"
    
    # Somewhere else
    async def main():
        loop = asyncio.get_event_loop()
        futures = [
            loop.run_in_executor(THREAD_POOL, synchronous_handler, param1, ...),
            loop.run_in_executor(THREAD_POOL, synchronous_handler, param1, ...),
            loop.run_in_executor(THREAD_POOL, synchronous_handler, param1, ...),
        ]
        await asyncio.wait(futures)
        for future in futures:
            print(future.result())
    
    with THREAD_POOL:
        loop = asyncio.get_event_loop()
        loop.run_until_complete(main())
    

Если вы не можете использовать потоки по какой-либо причине, то использование такой библиотеки просто делает весь асинхронный код бессмысленным.

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