目次
概要
Python標準の threading モジュールを使用して、複数の処理を並行して実行するマルチスレッドの実装方法を解説します。
API通信やファイルの読み書きなど、待ち時間(I/O待ち)が発生するタスクを効率よく処理するための基礎技術です。引数の渡し方(args/kwargs)や、バックグラウンド実行(デーモン化)についても触れます。
仕様(入出力)
- 入力: 並行実行させたい関数、およびその関数に渡す引数
- 出力: 複数のスレッドによる標準出力(ログ)、並行動作による処理完了
- 前提: Python標準ライブラリ
threadingを使用
基本の使い方
関数を定義し、Thread オブジェクトを作成して start() で開始、join() で終了を待機する基本的な流れです。
import threading
import time
def task():
time.sleep(1)
print("サブスレッドの処理完了")
# スレッドの作成と開始
t = threading.Thread(target=task)
t.start()
# スレッドの終了を待つ
t.join()
print("メインスレッド終了")
コード全文
「数字を出力する処理」と「文字を出力する処理」を同時に走らせる実装例です。
args(タプル)と kwargs(辞書)それぞれの引数の渡し方を網羅しています。
import threading
import time
def print_numbers(thread_name, loops):
"""指定された回数だけ数字を出力する関数"""
print(f"[{thread_name}] 開始")
for i in range(loops):
print(f"{thread_name}: {i}")
time.sleep(1) # 1秒待機
print(f"[{thread_name}] 完了")
def print_letters(thread_name, chars):
"""リスト内の文字を順番に出力する関数"""
print(f"[{thread_name}] 開始")
for char in chars:
print(f"{thread_name}: {char}")
time.sleep(1.5) # 1.5秒待機
print(f"[{thread_name}] 完了")
if __name__ == "__main__":
print("--- メイン処理開始 ---")
# 1. スレッドの作成
# args指定: 引数をタプルで渡す (要素が1つの場合もカンマが必要)
thread1 = threading.Thread(
target=print_numbers,
args=("Thread-Num", 3)
)
# kwargs指定: 引数を辞書で渡す
thread2 = threading.Thread(
target=print_letters,
kwargs={"thread_name": "Thread-Char", "chars": ["A", "B", "C"]}
)
# 2. スレッドの開始 (start)
thread1.start()
thread2.start()
print("--- スレッド実行中もメイン処理は動きます ---")
# 3. スレッドの終了待機 (join)
# これを実行すると、対象のスレッドが終わるまでここで停止します
thread1.join()
thread2.join()
print("--- 全てのスレッドが終了しました ---")
カスタムポイント
パラメータの意味
threading.Thread クラス生成時の主要な引数です。
| パラメータ | 意味・役割 | 設定例 |
| target | スレッドで実行したい関数オブジェクトを指定します。 | target=my_func |
| args | 関数に渡す引数を「タプル」で指定します。 | args=("data", 1) |
| kwargs | 関数に渡す引数を「辞書」で指定します。 | kwargs={"key": "value"} |
| daemon | デーモンスレッド(常駐化)にするか指定します。Trueの場合、メイン処理が終わると強制終了されます。 | daemon=True |
メソッドの役割
| メソッド | 意味・役割 |
thread.start() | スレッド処理を開始します。これを呼ばないと動きません。 |
thread.join() | スレッドの処理が終わるまで、呼び出し元の処理を一時停止(待機)させます。 |
注意点
- argsのカンマ忘れ
argsはタプルで渡す必要があります。引数が1つの場合、args=("hoge")ではなくargs=("hoge",)とカンマを付けないとエラーになります。
- CPUバウンドな処理
- Pythonの仕様(GIL: Global Interpreter Lock)により、計算量が多い処理(CPUバウンド)をマルチスレッド化しても速度は向上しません。計算処理の並列化には
multiprocessingを使用してください。
- Pythonの仕様(GIL: Global Interpreter Lock)により、計算量が多い処理(CPUバウンド)をマルチスレッド化しても速度は向上しません。計算処理の並列化には
- 競合状態(Race Condition)
- 複数のスレッドが同じ変数(リストやグローバル変数)を同時に書き換えると、データが整合性を失う可能性があります。必要に応じて
threading.Lockで排他制御を行ってください。
- 複数のスレッドが同じ変数(リストやグローバル変数)を同時に書き換えると、データが整合性を失う可能性があります。必要に応じて
応用
デーモンスレッド (daemon=True) の使用例です。
定期実行などの終わりのないタスクを実行する際、メインプログラムの終了と同時に道連れで終了させたい場合に使います。
import threading
import time
def background_task():
"""終わりのない監視タスクなどを想定"""
while True:
print("[Daemon] 監視中...")
time.sleep(0.5)
if __name__ == "__main__":
# daemon=True に設定
t = threading.Thread(target=background_task, daemon=True)
t.start()
print("メイン処理: 2秒間だけ動作します")
time.sleep(2)
print("メイン処理: 終了")
# ここでメインが終わると、background_taskも強制的に停止します
# (join() しないのがポイント)
まとめ
threading モジュールを使えば、通信待ちなどの時間を有効活用してプログラム全体の効率を上げることができます。
基本は target で関数を指定し、start() で開始、join() で待機です。バックグラウンドで動かし続けたい場合のみ daemon=True を活用してください。
