memo

2016-01-27

python で同期的なコードの中で asyncio な処理を実行したい

twisted の reactor とは違って、 asyncio の event loop はスレッドごとに独立してるっぽい (get_event_loop() で出てくるものが thread 毎に別っぽい) のと、 好きに新しく作ったり用が済んだら止めたりできるっぽい。

なので、同期的なコードの中で event loop を作成して、 その上で非同期なコードを実行して用が済んだら止める、 なんてことが簡単にできる。

ということの確認:

import asyncio

from contextlib import contextmanager


@contextmanager
def with_loop():
    # 新しく event loop を作成してセットして、
    # 用が済んだら閉じる context manager
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)

    yield loop

    loop.close()
    asyncio.set_event_loop(None)


def test():
    # この関数自体は通常の同期的な処理

    import aiohttp

    async def get(url):
        # aiohttp を使って非同期に HTTP request を投げ、
        # response body を返す処理
        response = await aiohttp.get(url)
        body = await response.read()
        return body

    with with_loop() as loop:
        # HTTP request を複数同時に投げるような例
        tasks = [
            get('http://localhost/'),
            get('http://localhost/'),
        ]
        # 作成した event loop を使って非同期処理を実行し、結果を受ける
        results = loop.run_until_complete(asyncio.gather(*tasks))

    print(results)


if __name__ == '__main__':
    import threading

    # test 関数は通常の関数なので、普通に thread を使って実行できる、
    # ということの確認。
    t1 = threading.Thread(target=test)
    t2 = threading.Thread(target=test)
    t1.start()
    t2.start()
    t1.join()
    t2.join()

動作はしているけど、 event loop を作って走らせて閉じてする負荷はどんなもんなんだろう?