Threadクラス利用のサンプル
| 登録日 | :2025/05/06 13:20 |
|---|---|
| カテゴリ | :Python基礎 |
Threadクラスを継承して作成するクラスについて、処理内容のリソースを共有する場合、例えば、Queueを使う場合はどうのようなコードを書くよ良いでしょうか。
Threadクラスを継承して作成するクラスで、処理内容のリソースを共有したい場合(たとえばQueueを使ったワーカースレッドの実装)は、Queueオブジェクトを複数スレッドで共有し、各スレッドのrunメソッド内でQueueからデータを取り出して処理するのが一般的です。
import threading
import queue
import time
# 共有Queueの作成
task_queue = queue.Queue()
# Threadクラスを継承したワーカークラス
class WorkerThread(threading.Thread):
def __init__(self, name, task_queue):
super().__init__()
self.name = name
self.task_queue = task_queue
def run(self):
while True:
try:
# キューからタスクを取得(3秒待ってもなければ終了)
task = self.task_queue.get(timeout=3)
except queue.Empty:
print(f"{self.name}: キューが空なので終了します")
break
print(f"{self.name}: {task} を処理中")
time.sleep(1) # 処理のシミュレーション
self.task_queue.task_done()
# キューにタスクを追加
for i in range(10):
task_queue.put(f"タスク{i}")
# 複数のワーカースレッドを生成して開始
threads = []
for i in range(3):
t = WorkerThread(f"Worker-{i}", task_queue)
t.start()
threads.append(t)
# 全てのタスクが完了するのを待つ
task_queue.join()
# スレッドの終了を待機
for t in threads:
t.join()
print("全てのタスクが完了しました")
ポイント
Queueオブジェクトはスレッド間で安全に共有できる(内部でロック制御されているため、明示的なLockは不要)。
Threadクラスを継承したクラスの__init__でQueueを受け取り、runメソッドでgetして処理します。
task_queue.join()で全タスクの完了を待機でき、task_done()で処理完了を通知します。
このように、Queueを使えばThreadクラス継承型でも安全かつ簡単にリソース共有・並列処理が実現できます
task_queue.join()は必要です。
理由
task_queue.join()は、「キューに入れた全てのタスクが、ワーカースレッドによって処理完了(task_done()が呼ばれる)するまで、メインスレッドを待機させる」ためのものです。
これがないと、メインスレッドがワーカースレッドの処理完了を待たずに次の処理や終了に進んでしまう可能性があります。
まとめ
全てのタスクが確実に処理されるまで待ちたい場合は、task_queue.join()は必須です。
逆に、待つ必要がない場合や、他の方法で終了管理する場合は省略できますが、通常は入れておくのが安全です。
OSスレッド(特にプロセス)は、限られた数の時間のかかるタスクに適している。
大量のスレッドを使用すると、スレッドのスタックサイズに関連して繰り返されるコンテキストの切り替えやメモリの消費が原因で、パフォーマンスの低下に拍車をかけるをかける可能性がある。
コストのかかるスレッドやプロセスの作成という問題を打破するには単純なアプローチの一つは、「busy wait」アプローチ。ノンブロッキング処理を使うことで、一つのスレッドで複数のクライアントリクエストを同時に処理ができる。(asyncioを使うなど)