Python の RLock で再入可能なロックを使う
RLock(Reentrant Lock、再入可能ロック)は、同じスレッドから複数回取得できるロックです。通常の Lock では同じスレッドが2回ロックを取得しようとするとデッドロックになりますが、RLock ではそれを避けられます。
Lock と RLock の違い
通常の Lock は、同じスレッドであっても2回取得しようとするとブロックされます。
import threading
lock = threading.Lock()
def outer():
with lock:
print("outer")
inner() # ここでデッドロック!
def inner():
with lock: # 同じスレッドでも再取得できない
print("inner")
# outer() # これを実行するとデッドロックRLock を使えば、同じスレッドから何度でもロックを取得できます。
import threading
rlock = threading.RLock()
def outer():
with rlock:
print("outer")
inner()
def inner():
with rlock: # 同じスレッドなので取得できる
print("inner")
outer()
# outer
# innerRLock の動作
Lock
同じスレッドでも2回目の acquire でブロックされる。
RLock
同じスレッドなら何度でも acquire できる。acquire と同じ回数だけ release が必要。
RLock は内部でカウンターを持っており、acquire() の回数と同じだけ release() を呼ぶ必要があります。
import threading
rlock = threading.RLock()
rlock.acquire()
print("1回目取得")
rlock.acquire()
print("2回目取得")
rlock.acquire()
print("3回目取得")
rlock.release()
print("1回目解放")
rlock.release()
print("2回目解放")
rlock.release()
print("3回目解放(完全に解放)")実用例:再帰的な処理
再帰的にロックを必要とする処理で RLock が役立ちます。
import threading
class TreeNode:
def __init__(self, value):
self.value = value
self.children = []
self._lock = threading.RLock()
def add_child(self, node):
with self._lock:
self.children.append(node)
def total(self):
with self._lock:
total = self.value
for child in self.children:
total += child.total() # 再帰的に呼び出し
return total
root = TreeNode(10)
root.add_child(TreeNode(5))
root.add_child(TreeNode(3))
print(root.total()) # 18実用例:メソッド間でロックを共有
複数のメソッドが同じロックを使い、互いに呼び出し合う場合に便利です。
import threading
class BankAccount:
def __init__(self, balance):
self.balance = balance
self._lock = threading.RLock()
def deposit(self, amount):
with self._lock:
self.balance += amount
print(f"入金: {amount}, 残高: {self.balance}")
def withdraw(self, amount):
with self._lock:
if self.balance >= amount:
self.balance -= amount
print(f"出金: {amount}, 残高: {self.balance}")
return True
return False
def transfer(self, other, amount):
with self._lock:
if self.withdraw(amount): # withdraw も同じロックを使う
other.deposit(amount)
account1 = BankAccount(1000)
account2 = BankAccount(500)
account1.transfer(account2, 200)いつ RLock を使うべきか
RLock が必要なケース
同じロックで保護されたメソッドを再帰的に呼び出す場合。メソッドが他のロック保護されたメソッドを呼ぶ場合。
Lock で十分なケース
単純な排他制御。再入の必要がない場合。パフォーマンスを重視する場合(Lock の方がわずかに高速)。
RLock は便利ですが、必要ない場合は通常の Lock を使う方がシンプルです。デッドロックのリスクがある場合にのみ RLock を検討しましょう。



