デッドロックは他のタスクによって占有されて実行できない状態
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
….