'Do I understand threading.Condition correctly?

I was working on this leetcode problem (https://leetcode.com/problems/fizz-buzz-multithreaded/). I eventually found a slower answer and then looked at the faster ones and found this...

class FizzBuzz:
    def __init__(self, n: int):
        self.n = n
        self.i = 1
        self.lock = threading.Lock()
        self.cond = threading.Condition(self.lock)

    # printFizz() outputs "fizz"
    def fizz(self, printFizz: 'Callable[[], None]') -> None:
        while True:
            with self.lock:
                self.cond.wait_for(lambda: self.i > self.n or (self.i % 3 == 0 and self.i % 5 != 0))
                if self.i > self.n:
                    break
                printFizz()
                self.i += 1
                self.cond.notify_all()


    # printBuzz() outputs "buzz"
    def buzz(self, printBuzz: 'Callable[[], None]') -> None:
        while True:
            with self.lock:
                self.cond.wait_for(lambda: self.i > self.n or (self.i % 3 != 0 and self.i % 5 == 0))
                print("b  a", self.i)
                if self.i > self.n:
                    break
                printBuzz()
                self.i += 1
                self.cond.notify_all()


    # printFizzBuzz() outputs "fizzbuzz"
    def fizzbuzz(self, printFizzBuzz: 'Callable[[], None]') -> None:
        while True:
            with self.lock:
                self.cond.wait_for(lambda: self.i > self.n or (self.i % 3 == 0 and self.i % 5 == 0))
                if self.i > self.n:
                    break
                printFizzBuzz()
                self.i += 1
                self.cond.notify_all()
        

    # printNumber(x) outputs "x", where x is an integer.
    def number(self, printNumber: 'Callable[[int], None]') -> None:
        while True:
            with self.lock:
                self.cond.wait_for(lambda: self.i > self.n or (self.i % 3 != 0 and self.i % 5 != 0))
                if self.i > self.n:
                    break
                printNumber(self.i)
                self.i += 1
                self.cond.notify_all()

I'm now trying to understand how threading.Condition.wait and threading.Condition.notify. This is what I think happens.

Each Condition is initialized with a reference to a threading.Lock. Two threads might use the same condition object or separate objects (with no effect on the below).

  1. The first thread calls .wait.
  2. .wait checks to make sure the lock has been acquired.
  3. .wait signals to the lock to release, and also that the condition is waiting on the lock.
  4. The lock allows another thread to acquire it.
  5. Eventually the other thread calls .notify.
  6. .notify signals each condition that is waiting on the lock that the lock is about to be released and signal the lock to release.
  7. The first thread calls lock.acquire, which blocks temporarily.
  8. The lock actually releases.
  9. The first thread's lock.acquire returns, unblocking that thread.

Is this correct?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source