web-dev-qa-db-ja.com

Python 3.5実際のコード例によるasync / await

Pythonの3.5 async/awaitについての記事やチュートリアルをたくさん読みました。 get_event_loop()とrun_until_complete()を使用するもの、ensure_future()を使用するもの、asyncio.wait()を使用するもの、call_soon()を使用するものがあるため、かなり混乱していると言わざるを得ません。

私は多くの選択肢があるように見えますが、それらが完全に同一であるか、ループを使用する場合とwait()を使用する場合があるかはわかりません。

しかし、すべての例は、待機可能なオブジェクトを返す実際の遅い操作のシミュレーションとして、asyncio.sleep()で動作します。この行を実際のコードと交換しようとすると、全体が失敗します。一体何が上に書かれたアプローチの違いであり、非同期/待機の準備ができていないサードパーティのライブラリをどのように実行すべきかです。 Quandlサービスを使用して在庫データを取得しています。

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

私がどれほど失われたと感じているか、並行して走りたいと思っているかどうか、あなたがポイントを得ることを願っています。

26
n1_

サードパーティのライブラリが_async/await_と互換性がない場合、明らかに簡単に使用することはできません。次の2つのケースがあります。

  1. ライブラリ内の関数が非同期であり、コールバックを提供するとしましょう。

    _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オブジェクトでも機能することに注意してください。 wrapperasyncとして宣言する必要はありません。

  2. ライブラリ内の関数が同期の場合、別のスレッドで実行できます(おそらく、そのためのスレッドプールを使用します)。コード全体は次のようになります。

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

何らかの理由でスレッドを使用できない場合、そのようなライブラリを使用すると、非同期コード全体が無意味になります。

ただし、非同期で同期ライブラリを使用することは、おそらく悪い考えです。あまり多くは得られませんが、コードはかなり複雑になります。

34
freakish