Pythonで非同期処理を使う(asyncio, Python 3.5以降)
この記事では、Python3.5以降で利用可能な非同期処理の仕組みであるasyncioについて解説します。async/await構文を用いた非同期プログラミングの基礎から、実践的な例までを分かりやすく説明します。
目次
- 非同期処理とは?
- asyncとawait
- asyncioの基本的な使い方
- タスクの並列実行
- awaitによるタスクの待ち合わせ
- gatherによる複数のタスクの同時実行
- 例外処理
- 実用的な例:複数のWebサイトへのアクセス
非同期処理とは?
非同期処理とは、複数のタスクを同時に実行することで、プログラム全体の処理時間を短縮する手法です。特にI/Oバウンドな処理(ネットワーク通信やファイル読み込みなど)が多い場合に効果を発揮します。Pythonでは、`asyncio`モジュールを用いて非同期処理を実装できます。
従来の同期処理では、各タスクが順番に実行されるため、I/Oバウンドな処理が完了するまで次の処理が待機することになり、全体の処理時間が長くなってしまいます。非同期処理では、タスクがブロックされることなく、他のタスクを実行できるため、効率的に処理を進めることができます。
asyncとawait
`async`キーワードは、非同期関数(コルーチン)を定義するために使用します。`await`キーワードは、非同期関数の結果が返されるまで待機します。ただし、`await`は、他のタスクが実行されることを妨げません。
import asyncio
async def my_coroutine():
print('Coroutine started')
await asyncio.sleep(1) # 1秒待機
print('Coroutine finished')
async def main():
await my_coroutine()
print('Main function finished')
asyncio.run(main())
asyncioの基本的な使い方
`asyncio.run()`関数を使って、非同期関数を走らせます。
import asyncio
async def my_coroutine():
print('Hello from coroutine!')
await asyncio.sleep(1)
asyncio.run(my_coroutine())
タスクの並列実行
複数のタスクを同時に実行するには、`asyncio.create_task()`を使ってタスクを作成し、`asyncio.gather()`を使って結果を待ちます。
import asyncio
async def task1():
await asyncio.sleep(1)
return 'Task 1 finished'
async def task2():
await asyncio.sleep(2)
return 'Task 2 finished'
async def main():
task1_future = asyncio.create_task(task1())
task2_future = asyncio.create_task(task2())
results = await asyncio.gather(task1_future, task2_future)
print(results)
asyncio.run(main())
awaitによるタスクの待ち合わせ
タスクが完了するのを待つには `await` を使います。
import asyncio
async def task():
await asyncio.sleep(1)
return 'Task finished'
async def main():
result = await task()
print(result)
asyncio.run(main())
gatherによる複数のタスクの同時実行
asyncio.gatherは複数のタスクを同時に実行し、結果をリストとして返します。
import asyncio
async def task(name, delay):
await asyncio.sleep(delay)
return f'{name} finished'
async def main():
results = await asyncio.gather(
task('Task 1', 1),
task('Task 2', 2),
task('Task 3', 0.5)
)
print(results)
asyncio.run(main())
例外処理
非同期関数内での例外処理は、`try...except`ブロックで行います。
import asyncio
async def task():
try:
await asyncio.sleep(1)
raise Exception('Something went wrong')
except Exception as e:
print(f'Exception caught: {e}')
async def main():
try:
await task()
except Exception as e:
print(f'Exception in main: {e}')
asyncio.run(main())
実用的な例:複数のWebサイトへのアクセス
非同期処理は、複数のWebサイトにアクセスする際に非常に効果を発揮します。
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://www.example.com', 'https://www.google.com']
async with aiohttp.ClientSession() as session:
results = await asyncio.gather(*[fetch_url(session, url) for url in urls])
print(results)
asyncio.run(main())
関連記事
- Pythonで非同期関数を定義する(async def, Python 3.5以降)
- PythonでFuture・Taskを使う(asyncio, Python 3.5以降)
- Pythonでコルーチンを使う(asyncio, Python 3.5以降)
- Pythonでスレッドプール・プロセスプールを使う(concurrent.futures)
- Pythonでスレッドを使う(threading)
- Pythonでプロセスを使う(multiprocessing)
- Pythonでスレッド間・プロセス間通信
- PythonでGIL(グローバルインタプリタロック)を理解する
- Pythonでaiohttpを使って非同期HTTPリクエストを送信 (Python 3.5以降)