Python asyncio入門|非同期処理の基本からasync・awaitまでわかりやすく解説
Pythonで複数の処理を効率よく実行したいときに役立つのがasyncioです。 asyncioを使うと、ネットワーク通信やAPI呼び出しなど、待ち時間の多い処理を効率よく実行できます。
本記事では、Python初心者にもわかりやすいように、asyncioの基本から async・await、asyncio.gather()までをサンプルコード付きで解説します。
Pythonのasyncioとは?
asyncio(アシンキオ)は、Pythonで非同期処理を実装するための標準ライブラリです。
通常のPythonプログラムでは、ある処理が終わるまで次の処理に進みません。 一方、asyncioを利用すると、ネットワーク通信などの待ち時間に別の処理を進められます。
処理A ──待ち時間──┐
処理B ──待ち時間──┤→ 効率的に処理
処理C ──待ち時間──┘
特に、I/O待ちが発生する処理で効果を発揮します。
asyncioが向いている処理
asyncioは、以下のような処理と相性がよいです。
- Web APIへのアクセス
- HTTP通信
- データベースへのアクセス
- ソケット通信
- ファイルI/O
- 複数サイトからのデータ取得
- 外部サービスとの通信
例えば、複数のWeb APIにアクセスする場合、それぞれのレスポンスを待っている時間を有効活用できます。
Python asyncioの基本
まずは最も基本的なasyncioプログラムを見てみましょう。
import asyncio
async def hello():
print ("こんにちは")
asyncio.run (hello ())
実行すると、以下のように表示されます。
こんにちは
ここで重要なのがasync defです。 async defを使うことで、非同期関数(コルーチン関数)を定義できます。
asyncとは?
asyncは、関数を非同期処理として定義するために使用します。
async def hello():
print ("Hello")
通常の関数ではdefを使用します。
def hello():
print ("Hello")
非同期関数ではasync defを使用します。 ただし、async defで定義しただけでは処理は実行されません。
基本的にはawaitやasyncio.run()を使って実行します。
awaitとは?
awaitは、非同期処理の完了を待つために使用します。
import asyncio
async def hello():
print ("開始")
await asyncio.sleep (1)
print ("終了")
asyncio.run (hello ())
実行すると、次のような流れになります。
開始
1秒待つ
終了
await asyncio.sleep (1)によって1秒間待機します。 この待機中に、イベントループは別の処理を進めることができます。
asyncio.sleep()を使ってみる
asyncio.sleep()は、asyncioを学習する際によく使われる関数です。
import asyncio
async def main():
print ("開始")
await asyncio.sleep (2)
print ("2秒後")
asyncio.run (main ())
通常のtime.sleep()とは異なり、 await asyncio.sleep (2)ではイベントループに制御を戻しながら待機できます。
asyncio.run()とは?
asyncio.run()は、非同期関数を実行するときに利用する代表的な関数です。
import asyncio
async def main():
print ("Python asyncio")
asyncio.run (main ())
Pythonでasyncioを始めるなら、 asyncio.run (main ())という基本パターンを覚えておくとよいでしょう。
asyncioで複数の処理を実行する
asyncioの大きなメリットは、複数の非同期処理を効率的に実行できることです。
import asyncio
async def task1():
print ("タスク1開始")
await asyncio.sleep (2)
print ("タスク1終了")
async def task2():
print ("タスク2開始")
await asyncio.sleep (2)
print ("タスク2終了")
async def main():
await task1 ()
await task2 ()
asyncio.run (main ())
この場合は、タスク1が終了してからタスク2が開始されるため、 合計で約4秒かかります。
asyncio.gather()で複数の処理を実行する
複数の非同期処理をまとめて実行するには、 asyncio.gather()が便利です。
import asyncio
async def task1():
print ("タスク1開始")
await asyncio.sleep (2)
print ("タスク1終了")
async def task2():
print ("タスク2開始")
await asyncio.sleep (2)
print ("タスク2終了")
async def main():
await asyncio.gather (
task1 (),
task2 ()
)
asyncio.run (main ())
この場合、2つのタスクが同時進行するため、 全体として約2秒で終了します。
タスク1 ───── 2秒 ───── 完了
タスク2 ───── 2秒 ───── 完了
ただし、これはCPU処理そのものが高速化しているわけではありません。 待ち時間を有効活用していることがポイントです。
asyncio.gather()の基本構文
基本的な構文は以下のとおりです。
await asyncio.gather (
task1 (),
task2 (),
task3 ()
)
複数の非同期処理をまとめて実行できます。
import asyncio
async def download1():
await asyncio.sleep (1)
return "データ1"
async def download2():
await asyncio.sleep (2)
return "データ2"
async def download3():
await asyncio.sleep (1)
return "データ3"
async def main():
results = await asyncio.gather (
download1 (),
download2 (),
download3 ()
)
print (results)
asyncio.run (main ())
結果は以下のようになります。
['データ1', 'データ2', 'データ3']
asyncioと同期処理の違い
同期処理と非同期処理の違いを理解すると、asyncioのメリットがわかりやすくなります。
同期処理
import time
def task1():
time.sleep (2)
print ("task1")
def task2():
time.sleep (2)
print ("task2")
task1 ()
task2 ()
この処理は約4秒かかります。
非同期処理
import asyncio
async def task1():
await asyncio.sleep (2)
print ("task1")
async def task2():
await asyncio.sleep (2)
print ("task2")
async def main():
await asyncio.gather (
task1 (),
task2 ()
)
asyncio.run (main ())
こちらは2つの待機処理が同時進行するため、約2秒で終了します。
asyncioはCPU負荷の高い処理には向いていない
asyncioを使えば、どんな処理でも高速になるわけではありません。
例えば、以下のようなCPU負荷の高い処理です。
for i in range (100000000):
# 重い計算
pass
このような処理は、asyncioよりも用途に応じて multiprocessing、ProcessPoolExecutor、NumPy、GPU処理などを検討したほうがよい場合があります。
asyncioは基本的にI/O待ちの効率化が得意と考えるとわかりやすいでしょう。
asyncio.create_task()とは?
asyncio.create_task()を使うと、 コルーチンをタスクとしてスケジュールできます。
import asyncio
async def task (name):
print (f"{name}開始")
await asyncio.sleep (2)
print (f"{name}終了")
async def main():
task1 = asyncio.create_task (task ("タスク1"))
task2 = asyncio.create_task (task ("タスク2"))
await task1
await task2
asyncio.run (main ())
create_task()を使うことで、 複数の処理をイベントループ上で進行させることができます。
asyncio.gather()とcreate_task()の違い
asyncioを学習していると、gather()と create_task()の違いが気になるでしょう。
| 方法 | 特徴 |
|---|---|
await |
非同期処理の完了を待つ |
asyncio.gather() |
複数の処理をまとめて実行する |
asyncio.create_task() |
コルーチンをタスクとしてスケジュールする |
asyncio.run() |
非同期処理の入口として実行する |
複数の処理をまとめて実行したいだけなら、 まずはasyncio.gather()を覚えるとよいでしょう。
Python asyncioでAPI通信を効率化する
asyncioが活躍する代表例がAPI通信です。
複数のAPIへアクセスする場合、同期処理ではそれぞれのレスポンスを順番に待つことになります。
API A → 待つ
↓
API B → 待つ
↓
API C → 待つ
非同期処理では、複数の通信を同時進行させることができます。
API A ─────────┐
API B ─────────┼→ 結果を取得
API C ─────────┘
実際のHTTP通信では、非同期HTTPクライアントとして aiohttpなどを利用する方法があります。
import asyncio
import aiohttp
async def fetch (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:
results = await asyncio.gather (
*(fetch (session, url) for url in urls)
)
for result in results:
print (len (result))
asyncio.run (main ())
このように、複数のURLへ効率よくアクセスできます。
asyncioでよくあるエラー
RuntimeWarning: coroutine was never awaited
asyncio初心者が遭遇しやすいエラーの一つです。
async def hello():
print ("Hello")
hello ()
非同期関数を呼び出しただけで、awaitしていません。
非同期関数の中なら、次のようにします。
await hello ()
また、プログラムの入口ではasyncio.run()を使用できます。
asyncio.run (hello ())
asyncio.run() cannot be called from a running event loop
Jupyter Notebookなど、すでにイベントループが動いている環境では、 asyncio.run()でエラーになることがあります。
その場合、環境に応じて以下のように直接awaitする方法があります。
await main ()
Python asyncioを学ぶときのポイント
asyncio初心者は、いきなり複雑な非同期プログラムを書く必要はありません。 次の順番で学習すると理解しやすくなります。
1. asyncを理解する
async def main():
pass
2. awaitを理解する
await asyncio.sleep (1)
3. asyncio.run()を理解する
asyncio.run (main ())
4. asyncio.gather()を理解する
await asyncio.gather (
task1 (),
task2 ()
)
5. create_task()を理解する
task = asyncio.create_task (task_function ())
6. API通信などで実践する
この順番で学ぶと、asyncioの仕組みを段階的に理解できます。
asyncioとthreadingの違い
Pythonで並行処理を考えるとき、asyncioとthreadingの違いも重要です。
| 項目 | asyncio | threading |
|---|---|---|
| 方式 | 非同期処理 | スレッド |
| 得意分野 | I/O処理 | I/O処理など |
| 並行処理 | イベントループ | 複数スレッド |
| コード | async / await | Threadなど |
| 主な用途 | 大量のI/O処理 | I/O処理・既存コードとの組み合わせなど |
asyncioを使うメリット
1. I/O待ちを有効活用できる
ネットワーク通信などの待ち時間に別の処理を進められます。
2. 大量の通信処理と相性がよい
APIやWebサイトなど、複数の外部サービスへアクセスするプログラムに向いています。
3. Python標準ライブラリで利用できる
基本的な非同期処理は、Python標準ライブラリのasyncioで実装できます。
4. async/awaitでコードを書ける
非同期処理を比較的読みやすい形で記述できます。
asyncioを使う際の注意点
asyncioを使う場合、すべての関数をasyncにすればよいわけではありません。
特に注意したいのがブロッキング処理です。
例えば、以下のような処理です。
time.sleep (5)
このような処理を非同期処理の中で実行すると、 イベントループを止めてしまう可能性があります。
非同期処理で待機する場合は、基本的に以下のようにします。
await asyncio.sleep (5)
また、同期的なライブラリを非同期コードに組み込む場合にも注意が必要です。
Python asyncio入門まとめ
Pythonのasyncioは、非同期処理を実装するための標準ライブラリです。
特に以下のような処理で力を発揮します。
- API通信
- Web通信
- データベースアクセス
- ソケット通信
- 外部サービスとの連携
- 大量のI/O処理
asyncioを学ぶうえでは、まず以下の4つを押さえておきましょう。
async def
await
asyncio.run ()
asyncio.gather ()
基本的な使い方をまとめると、以下のようになります。
import asyncio
async def task (name):
print (f"{name}開始")
await asyncio.sleep (2)
print (f"{name}終了")
async def main():
await asyncio.gather (
task ("タスク1"),
task ("タスク2")
)
asyncio.run (main ())
asyncioを理解するうえで重要なのは、 「待っている時間に別の処理を進める」という考え方です。
特にPythonでAPI連携、Webスクレイピング、AIサービスとの連携などを開発する場合、 asyncioを理解しておくと、より効率的なプログラムを設計できるようになります。
よくある質問(FAQ)
Pythonのasyncioとは何ですか?
asyncioは、Pythonで非同期処理を実装するための標準ライブラリです。 特にネットワーク通信など、I/O待ちが発生する処理で効果を発揮します。
asyncとawaitの違いは何ですか?
asyncは非同期関数を定義するために使い、 awaitは非同期処理の完了を待つために使用します。
asyncioはCPU処理の高速化にも使えますか?
asyncioは主にI/O待ちの効率化に向いています。 CPU負荷の高い計算処理の高速化には、multiprocessingなど別の方法を検討する必要があります。
asyncio.gather()とは何ですか?
複数の非同期処理をまとめて実行し、結果をまとめて取得するための関数です。
Python初心者でもasyncioを使えますか?
はい。まずはasync def、await、 asyncio.run()、asyncio.gather()の4つから学ぶと理解しやすくなります。






