web-dev-qa-db-fra.com

Python 3.5 asynchrone / attendre avec un exemple de code réel

J'ai lu des tonnes d'articles et un tutoriel sur la chose async/wait de Python 3.5. Je dois dire que je suis assez confus, car certains utilisent get_event_loop () et run_until_complete (), certains utilisent assure_future (), certains utilisent asyncio.wait () et certains utilisent call_soon ().

Il semble que j'ai beaucoup de choix, mais je n'ai aucune idée s'ils sont complètement identiques ou s'il y a des cas où vous utilisez des boucles et il y a des cas où vous utilisez wait ().

Mais la chose est que tous les exemples fonctionnent avec asyncio.sleep() comme simulation d'un fonctionnement lent réel qui retourne un objet attendable. Une fois que j'essaie d'échanger cette ligne pour du vrai code, tout échoue. Quelles sont les différences entre les approches écrites ci-dessus et comment dois-je exécuter une bibliothèque tierce qui n'est pas prête pour async/wait. J'utilise le service Quandl pour récupérer des données de stock.

 import asyncio
 import quandl

 async def slow_operation(n):
     # await asyncio.sleep(1) # Works because it's await ready.
     await quandl.Dataset(n) # Doesn't work because it's 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())

J'espère que vous comprendrez à quel point je me sens perdu et à quel point je voudrais avoir un fonctionnement simple en parallèle.

26
n1_

Si une bibliothèque tierce n'est pas compatible avec async/await, Vous ne pouvez évidemment pas l'utiliser facilement. Il y a deux cas:

  1. Disons que la fonction dans la bibliothèque est asynchrone et qu'elle vous donne un rappel, par ex.

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

    Vous pouvez donc faire:

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

    Dans ce cas, vous pouvez envelopper de telles fonctions dans le protocole asyncio comme ceci:

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

    (utilisez future.set_exception(exc) sur exception)

    Ensuite, vous pouvez simplement appeler ce wrapper dans une fonction async avec await:

    value = await wrapper(...)
    

    Notez que await fonctionne avec n'importe quel objet Future. Vous n'avez pas à déclarer wrapper comme async.

  2. Si la fonction dans la bibliothèque est synchrone, vous pouvez l'exécuter dans un thread séparé (vous utiliserez probablement un pool de threads pour cela). Le code entier peut ressembler à ceci:

    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())
    

Si vous ne pouvez pas utiliser de threads pour une raison quelconque, l'utilisation d'une telle bibliothèque rend tout simplement le code asynchrone inutile.

Notez cependant que l'utilisation d'une bibliothèque synchrone avec async est probablement une mauvaise idée. Vous n'obtiendrez pas grand-chose et pourtant vous compliquez beaucoup le code.

34
freakish