デッドロックは他のタスクによって占有されて実行できない状態
left_chopstickとright_chopstickを別々に処理しているのでこのようなことが起きる。left_chopstickとright_chopstickを同時にacuqire, releaseすればロジック上、ロックされない。
from threading import Lock
from typing import Any, Tuple
class LockWithName:
def __init__(self, name: str):
self.name = name
self._lock = Lock()
def acquire(self) -> None:
self._lock.acquire()
def release(self) -> None:
self._lock.release()
def locked(self) -> bool:
return self._lock.locked()
def __enter__(self) -> None:
self.acquire()
def __exit__(self, *args: Tuple[Any]) -> None:
self.release()
import time
from threading import Thread
from lock_with_name import LockWithName
dumplings = 20
class Philosopher(Thread):
def __init__(self, name: str,
left_chopstick: LockWithName,
right_chopstick: LockWithName):
super().__init__()
self.name = name
self.left_chopstick = left_chopstick
self.right_chopstick = right_chopstick
def run(self) -> None:
global dumplings
while dumplings > 0:
self.left_chopstick.acquire()
print(f"{self.left_chopstick.name} grabbed by {self.name} "
f"now needs {self.right_chopstick.name}")
self.right_chopstick.acquire()
print(f"{self.right_chopstick.name} grabbed by {self.name}")
dumplings -= 1
print(f"{self.name} eats a dumpling. "
f"Dumplings left: {dumplings}")
self.right_chopstick.release()
print(f"{self.right_chopstick.name} released by {self.name}")
self.left_chopstick.release()
print(f"{self.left_chopstick.name} released by {self.name}")
print(f"{self.name} is thinking...")
time.sleep(0.1)
if __name__ == "__main__":
chopstick_a = LockWithName("chopstick_a")
chopstick_b = LockWithName("chopstick_b")
philosopher_1 = Philosopher("Philosopher #1", chopstick_a, chopstick_b)
philosopher_2 = Philosopher("Philosopher #2", chopstick_b, chopstick_a)
philosopher_1.start()
philosopher_2.start()
$ python3 deadlock.py
chopstick_a grabbed by Philosopher #1 now needs chopstick_b
chopstick_b grabbed by Philosopher #2 now needs chopstick_a
止まってしまう
ここではwaiterというclassを追加しているが、別にwaiterではなく、whileのループの中で同時にacquire, releaseできれば良い。
import time
from threading import Thread, Lock
from lock_with_name import LockWithName
dumplings = 20
class Waiter:
def __init__(self) -> None:
self.mutex = Lock()
def ask_for_chopsticks(self,
left_chopstick: LockWithName,
right_chopstick: LockWithName) -> None:
with self.mutex:
left_chopstick.acquire()
print(f"{left_chopstick.name} grabbed")
right_chopstick.acquire()
print(f"{right_chopstick.name} grabbed")
def release_chopsticks(self,
left_chopstick: LockWithName,
right_chopstick: LockWithName) -> None:
right_chopstick.release()
print(f"{right_chopstick.name} released")
left_chopstick.release()
print(f"{left_chopstick.name} released")
class Philosopher(Thread):
def __init__(self, name: str, waiter: Waiter,
left_chopstick: LockWithName,
right_chopstick: LockWithName):
super().__init__()
self.name = name
self.left_chopstick = left_chopstick
self.right_chopstick = right_chopstick
self.waiter = waiter
def run(self) -> None:
global dumplings
while dumplings > 0:
print(f"{self.name} asks waiter for chopsticks")
self.waiter.ask_for_chopsticks (
self.left_chopstick, self.right_chopstick)
dumplings -= 1
print(f"{self.name} eats a dumpling. "
f"Dumpling left: {dumplings}")
print(f"{self.name} returns chopsticks to waiter")
self.waiter.release_chopsticks (
self.left_chopstick, self.right_chopstick)
time.sleep(0.1)
if __name__ == "__main__":
chopstick_a = LockWithName("chopstick_a")
chopstick_b = LockWithName("chopstick_b")
waiter = Waiter()
philosopher_1 = Philosopher("Philosopher #1", waiter, chopstick_a, chopstick_b)
philosopher_2 = Philosopher("Philosopher #2", waiter, chopstick_b, chopstick_a)
philosopher_1.start()
philosopher_2.start()
$ python3 deadlock_arbitrator.py
Philosopher #1 asks waiter for chopsticks
chopstick_a grabbed
chopstick_b grabbed
Philosopher #1 eats a dumpling. Dumpling left: 19
Philosopher #1 returns chopsticks to waiter
chopstick_b released
chopstick_a released
….