
Pythonでプログラムを作っていると、「複数の処理を同時に実行したい」という場面に出会うことがあります。
例えば、データをダウンロードしながら画面の更新も続けたい、複数のファイルを並行して処理したい、といったケースです。
そんなときに活躍するのが Thread()
クラスです。
Thread()を使えば、メインプログラムとは別の「スレッド」で処理を実行でき、複数の作業を同時並行で進めることができます。
この記事では、Thread()の基本的な意味から使い方、引数のオプション、活用シーン、注意点までをわかりやすく丁寧に解説します。
Thread()とは?
Thread()
は、「この処理を別のスレッドで実行してください」という指示を出すためのクラスです。
通常、Pythonプログラムは上から順番に1つずつ処理を実行していきます。
しかし、Thread()を使うと、メインの処理と並行して別の処理を同時に走らせることができます。
例えるなら、「1人で家事をする」のがシングルスレッド、「お父さんが洗濯、お母さんが料理、子どもが掃除」のように家族で分担するのがマルチスレッドのイメージです。
Thread()を使うことで、プログラムの効率を上げたり、ユーザーインターフェースをよりスムーズに動かしたりできるようになります。
基本の使い方
Pythonの標準ライブラリ threading
を使った最も基本的な使い方は以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import threading import time def my_function(): for i in range(5): print(f"スレッド内の処理: {i}") time.sleep(1) # スレッドの作成 thread = threading.Thread(target=my_function) # スレッドの開始 thread.start() # メインの処理 for i in range(5): print(f"メインの処理: {i}") time.sleep(1) |
このコードの意味は次の通りです:
threading.Thread(target=my_function)
:my_function を別スレッドで実行するThreadオブジェクトを作成thread.start()
:作成したスレッドを実際に開始- メインの処理とスレッド内の処理が同時並行で実行される
このように、メインプログラムと別スレッドが同時に動作し、コンソールには両方の出力が交互に表示されます。
具体例①:引数を渡すスレッド
スレッドで実行する関数に引数を渡したい場合は、args
パラメータを使用します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import threading import time def countdown(name, count): for i in range(count, 0, -1): print(f"{name}: {i}") time.sleep(1) print(f"{name}: 完了!") # 複数のスレッドを作成 thread1 = threading.Thread(target=countdown, args=("タイマー1", 5)) thread2 = threading.Thread(target=countdown, args=("タイマー2", 3)) # スレッドを開始 thread1.start() thread2.start() # メインスレッドで他の作業 print("メイン: 他の作業をしています...") time.sleep(2) print("メイン: まだ作業中...") |
この例では:
args=("タイマー1", 5)
で countdown関数に name="タイマー1"、count=5 を渡している- 2つのスレッドが異なる引数で同じ関数を並行実行
- メインスレッドも独立して処理を続けている
実行すると、3つの処理(タイマー1、タイマー2、メイン処理)が同時に走ることが確認できます。
具体例②:スレッドの終了を待つ
join()
メソッドを使用すると、スレッドの処理が完了するまでメインプログラムを待機させることができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import threading import time def long_task(task_name, duration): print(f"{task_name} を開始しました") time.sleep(duration) print(f"{task_name} が完了しました") # スレッドを作成して開始 thread1 = threading.Thread(target=long_task, args=("重い処理1", 3)) thread2 = threading.Thread(target=long_task, args=("重い処理2", 2)) thread1.start() thread2.start() print("すべてのスレッドを開始しました") # すべてのスレッドの完了を待つ thread1.join() thread2.join() print("すべての処理が完了しました") |
この例では:
thread1.join()
で thread1 の完了を待つthread2.join()
で thread2 の完了を待つ- 両方のスレッドが完了してから「すべての処理が完了しました」が表示される
join()を使うことで、スレッドの処理結果が必要な場合や、すべての処理が終わってから次のステップに進みたい場合に便利です。
Thread()の主なオプション引数
threading.Thread()
では、スレッドの動作に関する様々な設定をすることができます。
「このスレッドをどのように実行するか」「どんな名前を付けるか」を調整するためのものです。
代表的なオプションを説明すると次の通りです:
オプション名 | 説明 | 例 |
---|---|---|
target | 実行したい関数を指定 | target=my_function |
args | 関数に渡す引数をタプルで指定 | args=(arg1, arg2) |
kwargs | 関数にキーワード引数を辞書で指定 | kwargs={"key": "value"} |
name | スレッドに名前を付ける(デバッグ時に便利) | name="DataProcessor" |
daemon | デーモンスレッドにするかどうか | daemon=True |
以下はそれらを組み合わせた例です:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
import threading import time def process_data(data, delay=1, verbose=False): thread_name = threading.current_thread().name if verbose: print(f"[{thread_name}] データ処理開始: {data}") time.sleep(delay) if verbose: print(f"[{thread_name}] データ処理完了: {data}") # 様々なオプションを指定してスレッドを作成 thread1 = threading.Thread( target=process_data, args=("ファイル1.txt",), kwargs={"delay": 2, "verbose": True}, name="FileProcessor-1" ) thread2 = threading.Thread( target=process_data, args=("ファイル2.txt",), kwargs={"delay": 1, "verbose": True}, name="FileProcessor-2", daemon=True ) thread1.start() thread2.start() # スレッド情報の確認 print(f"アクティブなスレッド数: {threading.active_count()}") for thread in threading.enumerate(): print(f"スレッド名: {thread.name}") |
この例では:
args
で位置引数、kwargs
でキーワード引数を両方指定name
でスレッドに分かりやすい名前を付与daemon=True
でデーモンスレッドに設定- スレッド情報を取得してデバッグに活用
このようにオプションを活用することで、より管理しやすく、デバッグしやすいマルチスレッドプログラムが作成できます。
Threadに関係する主要メソッド一覧
Thread()を効果的に活用するために、知っておくべき主要なメソッドを紹介します。
これらのメソッドを使いこなすことで、より柔軟で安全なマルチスレッドプログラムが作成できます。
スレッドの制御メソッド
メソッド名 | 説明 | 使用例 |
---|---|---|
start() |
スレッドを開始する | thread.start() |
join(timeout=None) |
スレッドの終了を待つ(任意でタイムアウト設定可能) | thread.join(5) |
is_alive() |
スレッドが実行中かどうかを確認 | if thread.is_alive(): |
スレッドの情報取得メソッド
メソッド名 | 説明 | 使用例 |
---|---|---|
getName() / name |
スレッド名を取得 | print(thread.name) |
setName(name) |
スレッド名を設定 | thread.setName("Worker-1") |
ident |
スレッドIDを取得(読み取り専用) | print(thread.ident) |
daemon |
デーモンスレッドかどうかの設定・取得 | thread.daemon = True |
threadingモジュールの関数
関数名 | 説明 | 使用例 |
---|---|---|
threading.current_thread() |
現在のスレッドオブジェクトを取得 | current = threading.current_thread() |
threading.active_count() |
アクティブなスレッド数を取得 | count = threading.active_count() |
threading.enumerate() |
全アクティブスレッドのリストを取得 | for t in threading.enumerate(): |
threading.main_thread() |
メインスレッドオブジェクトを取得 | main = threading.main_thread() |
活用シーン
Thread()は、様々な場面でプログラムの効率化や使い勝手の向上に役立ちます。具体的には次のようなケースです:
バックグラウンド処理
- ファイルのダウンロードや大量データの処理中も、UIの操作を継続可能
- ログの出力やデータベースへの定期的な保存処理
並行処理によるパフォーマンス向上
- 複数のAPIエンドポイントへの同時リクエスト
- 複数ファイルの並行読み込み・変換処理
リアルタイム処理
- チャットアプリでの メッセージ送受信 とUI更新の分離
- ゲームでの入力処理と画面描画の並行実行
定期実行処理
- 一定間隔でのデータ取得や状態チェック
- システム監視やヘルスチェック
このように、ユーザー体験の向上やシステムの効率化を図りたい場面で、Thread()は強力なツールとなります。
注意点
便利なThread()ですが、使うときにはいくつか気をつけるべきポイントがあります。
データの競合状態(Race Condition)
- 複数のスレッドが同じデータを同時に変更しようとすると、予期しない結果になる可能性があります。
- 共有データにアクセスする際は
threading.Lock()
などの同期機能を使用しましょう。
GIL(Global Interpreter Lock)の制約
- PythonのGILにより、CPUを多用する処理では真の並列実行ができない場合があります。
- CPU集約的な処理には
multiprocessing
モジュールの検討も必要です。
リソースの管理
- 作成したスレッドが正常に終了することを確認し、無限ループやデッドロックが発生しないよう注意が必要です。
デーモンスレッドの扱い
daemon=True
に設定したスレッドは、メインプログラムの終了と同時に強制終了されます。- 重要な処理の完了を保証したい場合は注意が必要です。
使いやすいThread()ですが、並行処理特有の問題を理解して、適切な同期機能を使うことが重要です。
まとめ
今回はPythonの Thread()
クラスの基本と応用について解説しました。
まとめると、次のポイントが重要です:
- Thread()は「別スレッドで処理を実行するための仕組み」
- 複数の処理を同時並行で実行できるため、プログラムの効率化が可能
- target、args、kwargs などの引数で柔軟な実行設定ができる
- join()メソッドでスレッドの完了を待つことができる
- name や daemon オプションで管理しやすいスレッドが作成できる
- データ競合やGILの制約など、並行処理特有の注意点を理解することが重要
Thread()を使えば、「この処理は別スレッドで実行して、メインの作業は継続する」というプログラムが簡単に書けるようになります。
重い処理があってもUIが固まらない、複数のタスクを並行実行できる、といったユーザーにとって快適なプログラムを作ることができるのが大きな魅力です。
初心者の方も、まずは「Thread()で処理を分けて同時実行する」ことから始めると、より効率的で使いやすいプログラム作りの流れが理解しやすくなりますよ!