LoginSignup
1
1

Pythonで非同期処理

Posted at

TypeScriptでフロントエンドの開発していた頃に非同期処理をゴリゴリ書いていましたが
今はPythonで同期処理ばかりで、Pythonで非同期処理が作成できないか調べてみました

Python3.11からasyncioというライブラリを使用して非同期処理が作成できるようになりました

1.asyncioについて

asyncio は async/await 構文を使い 並行処理の コードを書くためのライブラリです。

asyncio は、高性能なネットワークとウェブサーバ、データベース接続ライブラリ、分散タスクキューなどの複数の非同期Pythonフレームワークの基盤として使われています。

asyncio は多くの場合、 IOバウンドだったり高レベルの 構造化された ネットワークコードに完璧に適しています。

asyncio は次の目的で 高レベル API を提供しています:
・並行に Python コルーチンを起動 し、実行全体を管理する
・ネットワーク IO と IPC を執り行う
・subprocesses を管理する
・キュー を使ってタスクを分散する
・並列処理のコードを 同期 させる

これに加えて、ライブラリやフレームワークの開発者が次のことをするための低レベルAPIがあります:
・ネットワーク通信、サブプロセスの実行、OSシグナルの取り扱いなどのための非同期APIを提供するイベントループの作成と管理を行う
・Transportを使った効率的なprotocolを実装します
・コールバックを用いたライブラリとasync/await構文を使ったコードの橋渡し
(本文引用)

公式サイトの非同期処理のサンプルコードです

import asyncio

async def main():
    print('Hello ...')
    await asyncio.sleep(1)
    print('... World!')

asyncio.run(main())

2.非同期処理が役立つ業界

  • Webアプリ
    リクエストを並行して処理することができ、アプリのケーラビリティを向上させることができる
  • DBアクセス(I/O)
    I/Oが時間がかかる場合があるため、並列処理させることで他の処理を待たずにI/Oできます
  • バッチ処理
    大量のデータを処理するバッチジョブで並列処理させることができるので効率が上がります。
  • メッセージキュー
    一度に多量のメッセージを受信した場合に同時に処理することができる
  • IoT
    デバイスから送られてきたデータをリアルタイムに処理する必要があるため非同期処理が便利です
  • イベント処理
    ユーザの画面クリックなどの処理を受けつつ他の処理をすることができます
  • ネットワーク通信
    APIやプロトコルの通信では、レスポンスを待つ間に他の処理をすることができます

サンプルコード

ネットワーク通信を例に非同期処理をコードで紹介します

import asyncio
import aiohttp

async def fetch(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.text()

async def download_pages(urls):
    tasks = [fetch(url) for url in urls]
    # 複数の非同期タスクを並行して実行
    return await asyncio.gather(*tasks)

if __name__ == "__main__":
    urls = [
        "https://qiita.com/",
        "https://qiita.com/timeline",
        "https://qiita.com/trend"
    ]

    # 非同期で複数のウェブページをダウンロード
    results = asyncio.run(download_pages(urls))

    # 結果を表示
    for i, result in enumerate(results, 1):
        print(f"Page {i}:\n{result[:200]}...\n")

fetch関数でページをダウンロードしています。
download_pages関数のreturn await asyncio.gather(*tasks)
asyncio.gather(*tasks)で前のページのダウンロードを待たずに、今回だと3ページ分を同時並行でダウンロードしています。
gatherは全てのtask、今回の場合はダウンロード処理が終わるまで待ちます。

また、1つのtask実行時にエラーが発生すると処理が続行されませんが、asyncio.gather(*tasks, return_exceptions=True)とすることで1つのtaskのエラー時に他のtaskは続行されます。

GitHubで公開しているのでダウンロードできます

おわりに

光通信、4G、5Gの到来でネットワーキングや通信に関するアプリは普及してきています。現代のアプリは多くのユーザから一度に多くのリクエストを受けることがあります。そのため効率的な処理が求められます。そこで、今回のasyncioのような非同期処理の重要になってきます。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1