web-dev-qa-db-ja.com

非同期コルーチンで同期関数をラップするにはどうすればよいですか?

aiohttp を使用して、TCP要求を別のサーバーに送信するAPIサーバーを構築します。 TCPリクエストは同期であり、私の目的のためのブラックボックスです。したがって、私のリクエストはこれらのリクエストがAPI全体をブロックしていることです。モジュールリクエストを非同期コルーチンでラップする方法が必要です。 APIの残りをブロックしません。

そのため、単純な例としてsleepを使用するだけで、時間のかかる同期コードを何らかの方法で非ブロッキングコルーチンにラップする方法はありますか。

async def sleep_async(delay):
    # After calling sleep, loop should be released until sleep is done
    yield sleep(delay)
    return 'I slept asynchronously'
28
Zac Delventhal

最終的に、私は このスレッド で答えを見つけました。私が探していた方法は run_in_executor です。これにより、イベントループをブロックすることなく、同期関数を非同期で実行できます。

上記のsleepの例では、次のようになります。

import asyncio
from time import sleep
from concurrent.futures import ProcessPoolExecutor

async def sleep_async(loop, delay):
    # Can set executor to None if a default has been set for loop
    await loop.run_in_executor(ProcessPoolExecutor(), sleep, delay)
    return 'I slept asynchronously'

次の回答も参照してください-> コルーチンが期待される通常の関数をどのように呼び出すのですか?

28
Zac Delventhal

デコレータを使用して、同期バージョンを非同期バージョンにラップできます。

import time
from functools import wraps, partial


def wrap(func):
    @wraps(func)
    async def run(*args, loop=None, executor=None, **kwargs):
        if loop is None:
            loop = asyncio.get_event_loop()
        pfunc = partial(func, *args, **kwargs)
        return await loop.run_in_executor(executor, pfunc)
    return run

@wrap
def sleep_async(delay):
    time.sleep(delay)
    return 'I slept asynchronously'

またはaioify libを使用します

% pip install aioify

それから

@aioify
def sleep_async(delay):
    pass
11
ospider

遅すぎるかどうかはわかりませんが、デコレータを使用してスレッドで関数を実行することもできます。ただし、co-opブロッキングであるasyncとは異なり、まだco-opブロッキングではないことに注意してください。

def wrap(func):
    from concurrent.futures import ThreadPoolExecutor
    pool=ThreadPoolExecutor()
    @wraps(func)
    async def run(*args, loop=None, executor=None, **kwargs):
        if loop is None:
            loop = asyncio.get_event_loop()
        future=pool.submit(func, *args, **kwargs)
        return asyncio.wrap_future(future)
    return run
0
hpca01