Python - スレッド間通信

こんにちは、未来のPythonの魔法使いたち!今日、私たちはPythonでのスレッド間通信のワクワクする旅に出かけます。プログラミングに新手であるとしても心配しないでください – 私があなたの友好的なガイドで、すべてをステップバイステップに説明します。では、始めましょう!

Python - Inter-thread Communication

なぜスレッド間通信が必要でしょうか?

具体的な内容に入る前に、まずスレッド間通信が何のためのものか理解しましょう。あなたが大きなプロジェクトで働いているチームの一員だと想像してください。みんな異なる部分で働いていますが、時々情報を共有したり、努力を調整したりする必要があります。それは、プログラム中のスレッドがすることと全く同じです。スレッド間通信は、彼らがお互いに「話す」方法です。

イベントオブジェクト

スレッドが通信する最も簡単な方法の一つであるイベントオブジェクトから始めましょう。

イベントオブジェクトとは?

イベントオブジェクトは、セットされたりクリアされたりできるフラグのようなものです。スレッドはこのフラグがセットされるのを待ってから進むことができます。それは、通りを横切る前に緑の光を待つのに少し似ています。

イベントオブジェクトの使用方法

簡単な例を見てみましょう:

import threading
import time

def waiter(event):
print("Waiter: イベントがセットされるのを待っています...")
event.wait()
print("Waiter: イベントがセットされました!今すぐ進めます。")

def setter(event):
print("Setter: 3秒後にイベントをセットします...")
time.sleep(3)
event.set()
print("Setter: イベントをセットしました!")

# イベントオブジェクトを作成
e = threading.Event()

# スレッドを作成して開始
t1 = threading.Thread(target=waiter, args=(e,))
t2 = threading.Thread(target=setter, args=(e,))

t1.start()
t2.start()

# 両方のスレッドが終了するのを待機
t1.join()
t2.join()

print("Main thread: すべて完了しました!")

これを分解してみましょう:

  1. threadingモジュールをスレッドの作成に、timeモジュールを遅延の追加にインポートします。
  2. 二つの関数waitersetterを定義します。
  3. waiter関数は、event.wait()を使ってイベントがセットされるのを待ちます。
  4. setter関数は、3秒(ある作業をシミュレートするために)待ってから、event.set()を使ってイベントをセットします。
  5. イベントオブジェクトeを作成します。
  6. 二つのスレッドを作成し、それぞれの関数にイベントオブジェクトを渡します。
  7. 両方のスレッドを開始し、join()を使って彼らが終了するのを待ちます。

これを実行すると、waiterが待っている状態になり、3秒後にsetterがイベントをセットし、waiterが進むことが見られます。

コンディションオブジェクト

次に、コンディションオブジェクトについて見ていきましょう。それはイベントオブジェクトのより洗練されたいとことに似ています。

コンディションオブジェクトとは?

コンディションオブジェクトは、スレッドが特定の条件が真になるのを待つことができます。それは、パーティで特定の人が到着するのを待ってゲームを始めるのに少し似ています。

コンディションオブジェクトの使用方法

コンディションオブジェクトを使った例を見てみましょう:

import threading
import time
import random

# 共有リソース
items = []
condition = threading.Condition()

def producer():
global items
for i in range(5):
time.sleep(random.random())  # さまざまな生成時間をシミュレート
with condition:
items.append(f"Item {i}")
print(f"Producer added Item {i}")
condition.notify()  # 消費者にアイテムが利用可能になったことを通知

def consumer():
global items
while True:
with condition:
while not items:
print("Consumer is waiting...")
condition.wait()
item = items.pop(0)
print(f"Consumer removed {item}")
time.sleep(random.random())  # さまざまな消費時間をシミュレート

# スレッドを作成して開始
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)

producer_thread.start()
consumer_thread.start()

# プロデューサが終了するのを待機
producer_thread.join()

# 消費者を停止(無限ループになっているため)
consumer_thread.daemon = True

これを分解してみましょう:

  1. 共有リソースitemsとコンディションオブジェクトを作成します。
  2. producer関数はアイテムをリストに追加し、消費者に通知します。
  3. consumer関数はアイテムが利用可能になるのを待ち、それらを削除して「消費」します。
  4. with condition:を使って、自動的にコンディションのロックを獲得して解放します。
  5. condition.wait()はロックを解放し、通知を待ちます。
  6. condition.notify()は、待機しているスレッドを一つ覚ます。

この例は、プロデューサー-コンシューマーの典型的なシナリオを示しており、一つのスレッドがアイテムを生成し、もう一つのスレッドがそれらを消費します。

イベントオブジェクトとコンディションオブジェクトの比較

以下は、イベントオブジェクトとコンディションオブジェクトの簡単な比較です:

機能 イベント コンディション
目的 単純な信号 複雑な同期
状態 2進数(セット/クリア) 複数の状態を持つことができる
待機 スレッドはイベントがセットされるのを待つ スレッドは特定の条件を待つ
通知 待機しているすべてのスレッドに通知 一つまたはすべての待機スレッドに通知
使用例 単純な「行く/行かない」シナリオ プロデューサー-コンシューマー問題、複雑な同期

まとめ

おめでとうございます!あなたはもうPythonでのスレッド間通信の最初の一歩を踏み出しました。私たちは、イベントオブジェクトを使った単純な信号と、より複雑な同期シナリオに使うコンディションオブジェクトについてカバーしました。

覚えることを忘れず、練習は成功の鍵です。これらのオブジェクトを使った自分のプログラムを書いてみてください。もしかしたら、異なるユーザーを表すスレッドで簡単なチャットシステムを作ったり、イベントを使って交通信号機システムをシミュレートしたりできます。

スレッド間通信は最初は難しいかもしれませんが、時間と練習をすれば、あなたもまるでマエストロがオーケストラを指揮するようにスレッドを編鸺"}

Credits: Image by storyset