【並列処理】デッドロックの発生

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