Skip to content

Pythonの非同期処理とマルチスレッドについて

公開日:November 22, 2024更新日:November 28, 2024
pythoncoding📄

Pythonでパフォーマンスを向上させたり、複数のタスクを効率的に並行処理するためには「非同期処理」や「マルチスレッド」を活用することが重要です。ここでは、それぞれの仕組みについて、どのように使うかを詳しく見ていきましょう。

1. 非同期処理(Asyncio)

Pythonの非同期処理を扱うためのライブラリとして、asyncioが一般的に使用されます。非同期処理は、I/O待ちなどの遅延を伴う操作を効率的に処理するのに適しています。

非同期関数の定義

Pythonでは、async defを使って非同期関数を定義します。そして、awaitを使って他の非同期関数を呼び出します。

python
import asyncio

async def say_hello():
    await asyncio.sleep(1)
    print("Hello, World!")

# イベントループを実行
asyncio.run(say_hello())

このコードでは、asyncio.sleep(1)で1秒間の遅延を待機し、その後に「Hello, World!」を出力します。awaitは、他の非同期関数の処理が完了するまで待つために使います。

複数タスクの並行処理

非同期処理の利点は、複数のタスクを同時に実行できることです。以下の例では、複数の非同期タスクを並行して実行しています。

python
async def task1():
    await asyncio.sleep(2)
    print("Task 1 完了")

async def task2():
    await asyncio.sleep(1)
    print("Task 2 完了")

async def main():
    await asyncio.gather(task1(), task2())

asyncio.run(main())

このコードでは、task1task2が同時に実行されるため、タスク2が先に完了します。asyncio.gather()を使うことで、複数の非同期タスクを並行して実行することが可能です。

実用例:ウェブリクエストの並行処理

非同期処理は、ネットワークリクエストのようなI/Oバウンドの操作で特に有効です。例えば、複数のAPIリクエストを非同期に処理することで、待機時間を短縮することができます。

python
import asyncio
import aiohttp

async def fetch_url(session, url):
    async with session.get(url) as response:
        return await response.text()

async def main():
    urls = ["https://example.com", "https://example.org", "https://example.net"]
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_url(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
        for result in results:
            print(result)

asyncio.run(main())

この例では、aiohttpライブラリを使って複数のURLに対して並行にリクエストを送信しています。

2. マルチスレッド

Pythonでマルチスレッドを使用することで、複数のスレッドが並行して実行されるようになります。特にCPUバウンドのタスクで役立ちますが、PythonのGlobal Interpreter Lock (GIL)により、完全な並列処理は制限されることがあります。

スレッドの作成と実行

Pythonの標準ライブラリthreadingを使って、スレッドを作成することができます。

python
import threading
import time

def task(name):
    print(f"{name} 開始")
    time.sleep(2)
    print(f"{name} 終了")

# スレッドの作成
thread1 = threading.Thread(target=task, args=("スレッド1",))
thread2 = threading.Thread(target=task, args=("スレッド2",))

# スレッドの開始
thread1.start()
thread2.start()

# スレッドの終了を待つ
thread1.join()
thread2.join()

このコードでは、2つのスレッドが並行して実行され、「スレッド1」と「スレッド2」の開始と終了が表示されます。start()でスレッドを開始し、join()でスレッドの終了を待ちます。

マルチスレッドの注意点

  • GILの制約: PythonにはGIL(グローバルインタプリタロック)という制約があり、純粋なPythonコードでは複数のスレッドが同時に実行されることはありません。そのため、CPUバウンドの処理ではスレッドよりもマルチプロセスを使うほうが適しています。
  • スレッドの安全性: スレッドは共有メモリを使用するため、複数のスレッドが同時にデータにアクセスする場合には競合が発生しやすくなります。そのため、Lockオブジェクトを使ってデータの競合を防ぐことが重要です。
python
lock = threading.Lock()

def safe_task(name):
    with lock:
        print(f"{name} 安全に開始")
        time.sleep(1)
        print(f"{name} 安全に終了")

with lockを使うことで、コードブロック内での処理が他のスレッドと競合しないようにすることができます。

まとめ

非同期処理とマルチスレッドは、Pythonで並行処理を実現するための重要なツールです。非同期処理はI/Oバウンドの操作に適しており、複数のタスクを効率よく実行できます。一方、マルチスレッドはCPUバウンドのタスクに向いていますが、GILの制約に注意する必要があります。

次に、Pythonでマルチプロセス処理について学ぶことで、並列処理の理解をさらに深めることができます。これらの知識を使って、より効率的でスケーラブルなプログラムを書いていきましょう!