ブログ管理者のP.Hです!
今回はスレッドで排他制御する方法を紹介したいと思います。異なるスレッドから同タイミングで処理を衝突させたくない場合、排他制御を行うことで回避できます。
それでは排他制御の実装の仕方を説明していきます。
threading.Lockモジュールの使い方
排他制御は、下記のようにLock()モジュールを使用します。
threading.Lock().acquire() 排他制御したいコード threading.Lock().release()
注意しなければいけないことは、排他制御したいコードの処理時間が長いと、その期間はブロック状態となり順次処理となりますので、処理時間が長くなります。ですので、排他制御を行う部分は最小限にしましょう。排他制御をしている方が安全だからといって、大きな範囲で排他制御を行ってしまっては並列処理をしている意味がありません。
python:サンプルコード
from concurrent.futures import ThreadPoolExecutor import time import threading class Lock1: def __init__(self, l): self.l = l def run(self): while True: self.l.acquire() # 排他制御開始 print("Lock1 : lock acquire") time.sleep(1) print("Lock1 : lock release") self.l.release() # 排他制御解除 time.sleep(1) class Lock2: def __init__(self, l): self.l = l def run(self): while True: self.l.acquire() # 排他制御開始 print("Lock2 : lock acquire") time.sleep(1) print("Lock2 : lock release") self.l.release() # 排他制御解除 time.sleep(1) if __name__ == "__main__": lock = threading.Lock() # Lockモジュール生成 th_pool = [] th_pool.append(Lock1(lock).run) # 各スレッドにLockモジュールを引数として渡す th_pool.append(Lock2(lock).run) # 各スレッドにLockモジュールを引数として渡す with ThreadPoolExecutor(max_workers=2) as executor: for th in th_pool: executor.submit(th) # スレッド生成
結果は以下のようになります。Lock1 acqure ⇒ release 、Lock2 acqure ⇒ release の順番が守られていることがわかります。
Lock1 : lock acquire Lock1 : lock release Lock2 : lock acquire Lock2 : lock release Lock1 : lock acquire Lock1 : lock release Lock2 : lock acquire Lock2 : lock release Lock1 : lock acquire Lock1 : lock release
self.l.acquire()
と`self.l.release()をコメントアウトして実行してみましょう。結果は以下のようになります。Lock1 acqure ⇒ release の間にLock2の処理が入ってしまうことがわかります。
Lock1 : lock acquire Lock2 : lock acquire Lock1 : lock release Lock2 : lock release Lock1 : lock acquire Lock2 : lock acquire Lock1 : lock release Lock2 : lock release Lock1 : lock acquire Lock2 : lock acquire
以上で排他制御の説明は終了です。