Pythonでプログラムを書いていると、「通信の受信を待ち受けながら、同時に別の計算や画面更新を行いたい」という場面に遭遇します。
通常、Pythonは上から下へ順番に実行される(同期処理)ため、通信待ちが発生するとプログラム全体が止まってしまいます。これを解決するのが、threadingモジュールを使ったクラス化です。
本記事では、実戦で役立つ「スレッドを継承したクラスの作成と呼び出し方法」を解説します。
1. なぜ「クラス」をスレッド化するのか?
TCPやUDPの通信、あるいはセンサーの監視など、「いつ来るかわからないデータ」を待つ処理にはスレッドが最適です。
- ブロック回避: 受信待ち(
recvなど)の間も、メインの処理を止めずに済む。 - カプセル化: 通信設定(IPやポート)や状態管理を一つのクラスに閉じ込められる。
- 整理されたコード: 「待ち受け担当」と「メイン担当」で役割をハッキリ分けられる。
2. スレッドクラスの実装とメイン側での呼び出し
以下に、スレッドクラスの定義から、メイン側での呼び出しまでをまとめたコード例を示します。
import threading
import time
# --- 1. スレッドを継承したクラスの定義 ---
class NetworkReceiver(threading.Thread):
"""
バックグラウンドで通信(受信)を待ち続けるクラス
"""
def __init__(self, target_name):
super(NetworkReceiver, self).__init__()
self.target_name = target_name
self.is_running = True
self.daemon = True # メイン終了時にこのスレッドも強制終了させる設定
def run(self):
"""
start()を呼ぶと、このメソッドの中身が別スレッドで動き出す
"""
print(f" [Thread] {self.target_name}:受信待ちを開始しました...")
while self.is_running:
# 本来はここで socket.recv() などの通信待ちを行う
# 今回はデモとして3秒ごとに「データ受信」を表示
time.sleep(3)
print(f"\n [Thread] ★重要★ {self.target_name}からデータを受信しました!")
print(f" [Thread] {self.target_name}:スレッドを安全に終了しました。")
def stop(self):
"""スレッドを外部から止めるためのメソッド"""
self.is_running = False
# --- 2. メイン側での呼び出し(メインロジック) ---
if __name__ == "__main__":
print("=== メイン処理:開始 ===")
# スレッドクラスのインスタンス化
receiver = NetworkReceiver("TCP-Server")
# スレッドを開始(内部で run() が呼ばれる)
receiver.start()
# メインスレッドでの別の仕事
try:
for i in range(1, 11):
print(f"[Main] 重い計算やUI更新などを実行中... ({i}/10)")
# スレッド側が受信待ちをしていても、メインは止まらずに進む
time.sleep(1)
except KeyboardInterrupt:
print("\n[Main] ユーザーにより中断されました")
finally:
# スレッドを安全に停止させる
print("=== メイン処理:スレッドに停止命令を出します ===")
receiver.stop()
receiver.join() # スレッドが完全に終わるのを待つ
print("=== メイン処理:すべて完了しました ===")3. コードのポイント解説
① threading.Thread の継承
class MyClass(threading.Thread): とすることで、そのクラス自体がスレッドの機能を持つようになります。初期化時には必ず super().__init__() を呼びましょう。
② run() メソッド
ここが「別動隊」が動くステージです。start() を呼び出すと自動的にこのメソッドが実行されます。通信プログラムの場合、ここを while ループにして受信を待ち続けます。
③ daemon = True の設定
これをしておかないと、メインのプログラムが終わっても、受信待ちをしているスレッドが終了せずに残り続けてしまう(プロセスが死なない)ことがあります。
④ メイン側の自由
コードを見てわかる通り、receiver.start() を呼んだ直後から、メイン側は自分のループ処理(10秒間のカウント)に入っています。その間、3秒ごとにスレッド側から「受信した!」というメッセージが割り込んできます。これこそが並列処理の醍醐味です。
4. まとめ:同期から非同期へ
Pythonの標準的な書き方では「受信を待っている間は、次の行に進めない」という制約がありますが、スレッドクラスを使えばその壁を越えられます。
TCP/UDP通信を扱うエンジニアにとって、この手法は「メインの制御を止めずに通信を維持する」ための必須スキルと言えるでしょう。


コメント