1195. Fizz Buzz Multithreaded - cocoder39/coco39_LC GitHub Wiki

1195. Fizz Buzz Multithreaded

Option 1: condition

without self.cur > self.n, it can happen that a thread acquires lock, self.cur == self.n at the point, but condition_func is unable to be satisfied so the thread is blocked on wait() and releases lock. The other thread acquires the lock, increment self.cur then notifies the first thread. First thread evaluates condition, and condition is not able to be satisfied so it releases lock again. No thread is able to update self.cur anymore.

from threading import Condition

class FizzBuzz:
    def __init__(self, n: int):
        self.n = n
        self.cur = 1
        self.condition = Condition()

    # printFizz() outputs "fizz"
    def fizz(self, printFizz: 'Callable[[], None]') -> None:
        self.exe(lambda num: printFizz(), lambda num: num % 3 == 0 and num % 5 != 0)

    # printBuzz() outputs "buzz"
    def buzz(self, printBuzz: 'Callable[[], None]') -> None:
        self.exe(lambda num: printBuzz(), lambda num: num % 3 != 0 and num % 5 == 0)

    # printFizzBuzz() outputs "fizzbuzz"
    def fizzbuzz(self, printFizzBuzz: 'Callable[[], None]') -> None:
        self.exe(lambda num: printFizzBuzz(), lambda num: num % 3 == 0 and num % 5 == 0)

    # printNumber(x) outputs "x", where x is an integer.
    def number(self, printNumber: 'Callable[[int], None]') -> None:
        self.exe(lambda num: printNumber(num), lambda num: num % 3 != 0 and num % 5 != 0)

    def exe(self, print_fun, condition_func):
        with self.condition:
            while self.cur <= self.n:
                self.condition.wait_for(lambda: condition_func(self.cur) or self.cur > self.n)
                if self.cur > self.n:
                    return
                else:
                    print_fun(self.cur)
                    self.cur += 1
                    self.condition.notifyAll()

Option 2; barrier

from threading import Barrier

class FizzBuzz:
    def __init__(self, n: int):
        self.n = n
        self.barrier = Barrier(4)

    # printFizz() outputs "fizz"
    def fizz(self, printFizz: 'Callable[[], None]') -> None:
        self.exe(lambda num: printFizz(), lambda num: num % 3 == 0 and num % 5 != 0)

    # printBuzz() outputs "buzz"
    def buzz(self, printBuzz: 'Callable[[], None]') -> None:
        self.exe(lambda num: printBuzz(), lambda num: num % 3 != 0 and num % 5 == 0)

    # printFizzBuzz() outputs "fizzbuzz"
    def fizzbuzz(self, printFizzBuzz: 'Callable[[], None]') -> None:
        self.exe(lambda num: printFizzBuzz(), lambda num: num % 15 == 0)

    # printNumber(x) outputs "x", where x is an integer.
    def number(self, printNumber: 'Callable[[int], None]') -> None:
        self.exe(lambda num: printNumber(num), lambda num: num % 3 != 0 and num % 5 != 0)

    def exe(self, print_fun, condition_func):
        for num in range(1, self.n+1):
            if condition_func(num):
                print_fun(num)
            self.barrier.wait()

Option 3: semaphore

from threading import Semaphore

class FizzBuzz:
    def __init__(self, n: int):
        self.n = n
        self.fs = Semaphore(0)
        self.bs = Semaphore(0)
        self.fbs = Semaphore(0)
        self.ns = Semaphore(1)

    # printFizz() outputs "fizz"
    def fizz(self, printFizz: 'Callable[[], None]') -> None:
        for num in range(self.n//3 - self.n//15):
            self.fs.acquire()
            printFizz()
            self.ns.release()

    # printBuzz() outputs "buzz"
    def buzz(self, printBuzz: 'Callable[[], None]') -> None:
        for num in range(self.n//5 - self.n//15):
            self.bs.acquire()
            printBuzz()
            self.ns.release()

    # printFizzBuzz() outputs "fizzbuzz"
    def fizzbuzz(self, printFizzBuzz: 'Callable[[], None]') -> None:
        for num in range(self.n // 15):
            self.fbs.acquire()
            printFizzBuzz()
            self.ns.release()
            
    # printNumber(x) outputs "x", where x is an integer.
    def number(self, printNumber: 'Callable[[int], None]') -> None:
        for num in range(1, self.n+1):
            self.ns.acquire()
            if num % 15 == 0:
                self.fbs.release()
            elif num % 3 == 0:
                self.fs.release()
            elif num % 5 == 0:
                self.bs.release()
            else:
                printNumber(num)
                self.ns.release()

Option 4: Lock

from threading import Lock

class FizzBuzz:
    def __init__(self, n: int):
        self.n = n
        self.fs = Lock()
        self.bs = Lock()
        self.fbs = Lock()
        self.ns = Lock()
        self.fs.acquire()
        self.bs.acquire()
        self.fbs.acquire()

    # printFizz() outputs "fizz"
    def fizz(self, printFizz: 'Callable[[], None]') -> None:
        for num in range(self.n//3 - self.n//15):
            self.fs.acquire()
            printFizz()
            self.ns.release()

    # printBuzz() outputs "buzz"
    def buzz(self, printBuzz: 'Callable[[], None]') -> None:
        for num in range(self.n//5 - self.n//15):
            self.bs.acquire()
            printBuzz()
            self.ns.release()

    # printFizzBuzz() outputs "fizzbuzz"
    def fizzbuzz(self, printFizzBuzz: 'Callable[[], None]') -> None:
        for num in range(self.n // 15):
            self.fbs.acquire()
            printFizzBuzz()
            self.ns.release()
            
    # printNumber(x) outputs "x", where x is an integer.
    def number(self, printNumber: 'Callable[[int], None]') -> None:
        for num in range(1, self.n+1):
            self.ns.acquire()
            if num % 15 == 0:
                self.fbs.release()
            elif num % 3 == 0:
                self.fs.release()
            elif num % 5 == 0:
                self.bs.release()
            else:
                printNumber(num)
                self.ns.release()