PYTHON MEBY

Pythonで非同期処理を使う(asyncio, Python 3.5以降)

この記事では、Python3.5以降で利用可能な非同期処理の仕組みであるasyncioについて解説します。async/await構文を用いた非同期プログラミングの基礎から、実践的な例までを分かりやすく説明します。

目次

非同期処理とは?

非同期処理とは、複数のタスクを同時に実行することで、プログラム全体の処理時間を短縮する手法です。特に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())

関連記事