Pythonのコルーチンとは?

Pythonのコルーチンは、関数の一種で、途中で実行を一時停止し、後で再開できる特徴を持っています。これにより、非同期処理や協調的なマルチタスクが可能になります。コルーチンは、I/O操作や他の遅延処理を効率的に処理するために利用され、特に非同期プログラミングにおいて重要な役割を果たします。 この記事では、Pythonのコルーチンの基本概念、async/await構文を使ったコルーチンの定義方法、そして実際に非同期処理を行うためのasyncioモジュールについて解説します。

コルーチンの基本構造

コルーチンは、処理を一時停止することで他の処理に制御を渡し、再度実行可能になったときに中断した場所から再開できる関数です。これにより、長時間実行されるタスク(例えばネットワーク通信やファイルI/O)の待ち時間を有効に活用し、他のタスクを並行して処理できます。

コルーチンの基本

コルーチンはasyncキーワードを使って定義され、awaitキーワードで非同期処理を一時停止し、他の処理の完了を待ちます。

import asyncio
# コルーチンの定義
async def my_coroutine():
    print("処理開始")
    await asyncio.sleep(1)  # 非同期処理で1秒待機
    print("処理再開")
# イベントループでコルーチンを実行
asyncio.run(my_coroutine())

出力

処理開始
(1秒待機)
処理再開

この例では、async defでコルーチンmy_coroutine()を定義し、await asyncio.sleep(1)で1秒間処理を一時停止しています。asyncio.run()を使ってコルーチンをイベントループで実行しています。

asyncawaitの仕組み

  • async
    asyncは、関数をコルーチンとして定義するために使われます。このキーワードを使うと、関数はコルーチンとして扱われ、非同期に実行できるようになります。
  • await
    awaitは、コルーチン内で他のコルーチンや非同期タスクの完了を待つために使います。awaitを使うと、その時点で処理が一時停止し、他のタスクに制御が移ります。

コルーチンとジェネレータの違い

コルーチンとジェネレータは、どちらも関数の途中で処理を一時停止できる点で似ていますが、目的と使用方法に違いがあります。

  • ジェネレータは、yieldキーワードを使って値を一時的に返し、次回再開時に処理を続ける関数です。主にイテレータを作成するために使われます。
  • コルーチンは、awaitキーワードを使って非同期処理を一時停止し、他の処理の結果を待つために使われます。非同期処理や並行処理に適しています。

ジェネレータの例

def my_generator():
    yield 1
    yield 2
    yield 3
gen = my_generator()
for value in gen:
    print(value)

出力

1
2
3

このジェネレータは、yieldで値を返しながら、次の値の生成を待機します。コルーチンはこれとは異なり、I/O待ちなどの非同期処理を一時停止するために使用されます。

asyncioモジュールを使った非同期処理

Pythonで非同期処理を効率的に行うためには、asyncioモジュールが使われます。このモジュールは、イベントループを用いた非同期タスクの管理を簡単に行うためのツールです。

複数のコルーチンを並行して実行

asyncioでは、複数のコルーチンを並行して実行し、時間を効率的に使うことができます。await asyncio.gather()を使うと、複数のコルーチンを一度に実行できます。

import asyncio
async def task1():
    print("タスク1開始")
    await asyncio.sleep(2)
    print("タスク1完了")
async def task2():
    print("タスク2開始")
    await asyncio.sleep(1)
    print("タスク2完了")
async def main():
    await asyncio.gather(task1(), task2())
asyncio.run(main())

出力

タスク1開始
タスク2開始
タスク2完了
タスク1完了

この例では、task1()task2()が並行して実行されます。task1()が2秒待機する間にtask2()は1秒で終了し、全体の待ち時間が短縮されています。

コルーチンのキャンセル

非同期タスクを実行中に、そのタスクをキャンセルすることもできます。asynciocancel()メソッドを使って、実行中のコルーチンをキャンセルできます。

import asyncio
async def long_running_task():
    try:
        print("長時間タスク開始")
        await asyncio.sleep(10)
        print("タスク完了")
    except asyncio.CancelledError:
        print("タスクがキャンセルされました")
async def main():
    task = asyncio.create_task(long_running_task())
    await asyncio.sleep(2)
    task.cancel()  # タスクをキャンセル
    await task  # タスクの終了を待つ
asyncio.run(main())

出力

長時間タスク開始
タスクがキャンセルされました

この例では、long_running_task()が10秒間実行される予定ですが、2秒後にキャンセルされます。

コルーチンの実用例

非同期Webリクエスト

非同期処理の代表的なユースケースは、複数のWebリクエストを並行して処理する場合です。aiohttpライブラリを使うと、非同期でHTTPリクエストを送信できます。

import aiohttp
import asyncio
async def
 fetch_url(session, url):
    async with session.get(url) as response:
        print(f"URL: {url}, ステータス: {response.status}")
        return await response.text()
async def main():
    async with aiohttp.ClientSession() as session:
        urls = ['https://www.example.com', 'https://www.python.org']
        tasks = [fetch_url(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
asyncio.run(main())

出力

URL: https://www.example.com, ステータス: 200
URL: https://www.python.org, ステータス: 200

この例では、aiohttpを使って複数のURLに対して非同期にリクエストを送信し、それぞれのステータスコードを取得しています。複数のリクエストが並行して処理されるため、全体の待ち時間が短縮されます。

非同期ファイル操作

ファイル操作も非同期で実行できます。aiofilesライブラリを使うことで、ファイルの読み書きを非同期に行えます。

import aiofiles
import asyncio
async def write_to_file():
    async with aiofiles.open('output.txt', 'w') as f:
        await f.write('非同期ファイル書き込み')
async def read_from_file():
    async with aiofiles.open('output.txt', 'r') as f:
        content = await f.read()
        print(content)
async def main():
    await write_to_file()
    await read_from_file()
asyncio.run(main())

出力

非同期ファイル書き込み

この例では、aiofilesを使って非同期にファイルに書き込み、その後ファイルの内容を読み取っています。非同期でのファイル操作は、特に大量のファイルを扱う場合に有効です。

結論

Pythonのコルーチンは、非同期処理や協調的マルチタスクを実現するための強力なツールです。async/await構文とasyncioモジュールを使うことで、複数のタスクを並行して効率的に処理できます。非同期Webリクエストやファイル操作など、コルーチンの適用範囲は広く、特にI/O待ちが発生する処理において大きな効果を発揮します。 コルーチンを使って、Pythonプログラムのパフォーマンスを最大限に引き出し、効率的な非同期処理を実装してみましょう。