PythonのスレッドをEventオブジェクトでブロックしてみよう
前回はスレッドの join()メソッドを使ってスレッドの待ち合わせをしました。
今回はプログラムの動きは前回と同じですが、Eventオブジェクトを使ってスレッドの動きをブロックしてみましょう。
イベントとは何か?
ここでいうイベントとは、Pythonのドキュメントを参照すると、以下のように記載されています。
イベントは、あるスレッドがイベントを発信し、他のスレッドはそれを待つという、スレッド間で通信を行うための最も単純なメカニズムの一つです。
イベントを使うと、前回の join()メソッドのように他のスレッドの終了まで待つのではなく、指定したタイミングでスレッドの中断や再開ができます。
Eventオブジェクトを作成する
Eventオブジェクトの使い方、以下の通りです。
オプションはありません。
オブジェクト名 = threading.Event()
スレッドをブロックする
スレッドをブロックするには、wait()メソッドを使います。
内部フラグの値が true になるまで、スレッドの動きをブロックします。
オプションの timeout を設定すると、明示的にブロックを解除しなくても、指定した時間になるとブロックが解除されます。
timeoutは、秒を表す浮動小数点です。
なお、ブロックするスレッドは、wait()メソッドを使ったスレッド自身です。
オブジェクト名.wait(timeout=None)
スレッドのブロックを解除する
スレッドのブロックを解除するには、set()メソッドを使います。
set()メソッドを使うことにより、内部フラグが true になり、ブロックされていたスレッドが動き出します。
オブジェクト名.set()
スレッドを再度ブロックする
一度 set()メソッドを使ってスレッドのブロックを解除すると、再度 wait()メソッドを使ってもスレッドはブロックされません。
再度スレッドをブロックしたい場合は、clear()メソッドを使って内部フラグを false にする必要があります。
オブジェクト名.clear()
スレッドをEventオブジェクトでブロックするプログラム
Eventオブジェクトを使ったプログラムは、以下のようになります。
動きは前回のプログラムと同じで、thread1が終了してからthread2が動きます。
wait()メソッドは、自分自身のスレッドをブロックすることに注意してください。
当たり前ですが、ブロックの解除は他のスレッドから行います。
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
### インポート import time import tkinter import threading ### 関数 def func(num, gap, pos_x, pos_y): ### 変数宣言 cnt = 0 ### thread2の場合 if threading.current_thread().name == "thread2": ### スレッドブロック event.wait() ### 10未満の間ループ while cnt < 10: ### カウントアップ cnt += num ### キャンバス書き込み id = canvas.create_text(pos_x, pos_y, text=cnt, font=(None,48)) ### 待ち時間 time.sleep(gap) ### キャンバス初期化 canvas.delete(id) ### thread1の場合 if threading.current_thread().name == "thread1": ### スレッドブロック解除 event.set() ### キャンバス作成 canvas = tkinter.Canvas(master=None, width=420, height=200) ### キャンバス表示 canvas.pack() ### イベント作成 event = threading.Event() ### スレッド作成 thread1 = threading.Thread(target=func, args=(1,1,140,100), name="thread1", daemon=True) thread2 = threading.Thread(target=func, args=(1,1,280,100), name="thread2", daemon=True) ### スレッド開始 thread1.start() thread2.start() ### イベントループ canvas.mainloop() |
プログラムを実行すると、左側の表示が終わるのを待ち、右側の表示が開始されます。
次にclear()メソッドを使って、thread2を一旦動かしたあと、再度停止させてみましょう。
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
### インポート import time import tkinter import threading ### 関数 def func(num, gap, pos_x, pos_y): ### 変数宣言 cnt = 0 ### thread2の場合 if threading.current_thread().name == "thread2": ### スレッドブロック event.wait() ### 10未満の間ループ while cnt < 10: ### カウントアップ cnt += num ### キャンバス書き込み id = canvas.create_text(pos_x, pos_y, text=cnt, font=(None,48)) ### 待ち時間 time.sleep(gap) ### thread1のカウンタが4か10の場合 if threading.current_thread().name == "thread1" and (cnt == 2 or cnt == 10): ### スレッドブロック解除 event.set() ### thread2のカウンタが6の場合 if threading.current_thread().name == "thread2" and cnt == 6: ### 内部フラグクリア event.clear() ### スレッド再ブロック event.wait() ### キャンバス初期化 canvas.delete(id) ### キャンバス作成 canvas = tkinter.Canvas(master=None, width=420, height=200) ### キャンバス表示 canvas.pack() ### イベント作成 event = threading.Event() ### スレッド作成 thread1 = threading.Thread(target=func, args=(1,1,140,100), name="thread1", daemon=True) thread2 = threading.Thread(target=func, args=(1,1,280,100), name="thread2", daemon=True) ### スレッド開始 thread1.start() thread2.start() ### イベントループ canvas.mainloop() |
プログラムを実行すると、左側の値が3の時に右側の値が1となり、右側の値が6の時に一旦停止して、左側の処理が終了すると再び右側の値がカウントアップしていきます。