DEV Community

michael-2509
michael-2509

Posted on

Multi-Threaded FizzBuzz

You have the four functions:

  • printFizz that prints the word "fizz" to the console,

  • printBuzz that prints the word "buzz" to the console,

  • printFizzBuzz that prints the word "fizzbuzz" to the console, and

  • printNumber that prints a given integer to the console.

You are given an instance of the class FizzBuzz that has four functions: fizz, buzz, fizzbuzz and number. The same instance of FizzBuzz will be passed to four different threads:

  • Thread A: calls fizz() that should output the word "fizz".

  • Thread B: calls buzz() that should output the word "buzz".

  • Thread C: calls fizzbuzz() that should output the word "fizzbuzz".

  • Thread D: calls number() that should only output the integers.

Modify the given class to output the series [1, 2, "fizz", 4, "buzz", ...] where the ith token (1-indexed) of the series is:

  • "fizzbuzz" if i is divisible by 3 and 5,

  • "fizz" if i is divisible by 3 and not 5,

  • "buzz" if i is divisible by 5 and not 3, or

  • i if i is not divisible by 3 or 5.

Implement the FizzBuzz class:

  • FizzBuzz(int n) Initializes the object with the number n that represents the length of the sequence that should be printed.

  • void fizz(printFizz) Calls printFizz to output "fizz".

  • void buzz(printBuzz) Calls printBuzz to output "buzz".

  • void fizzbuzz(printFizzBuzz) Calls printFizzBuzz to output "fizzbuzz".

  • void number(printNumber) Calls printnumber to output the numbers.

Example 1:

Input: n = 15
Output: [1,2,"fizz",4,"buzz","fizz",7,8,"fizz","buzz",11,"fizz",13,14,"fizzbuzz"]
Enter fullscreen mode Exit fullscreen mode

Example 2:

Input: n = 5
Output: [1,2,"fizz",4,"buzz"]
Enter fullscreen mode Exit fullscreen mode

Logic

  • loop through n , n times.

  • check for every value of n ranging from 1 to n, if it mets the condition

  • Print out the right word.

Code Logic

for i in range(1,self.n+1):
            if "condition is true":
                "printWord()"
Enter fullscreen mode Exit fullscreen mode

Let's understand this First!
Let's assume the order of thread execution is as follows Thread A, Thread B, Thread C and Thread D. The first step would be to write the logic of the code in the each of methods that run our functions to suit what each method is suppose to print out.

We loop through n and check if each value of n ranging from 1 to n mets the condition in the method. Unfortunately, Knowing the order of the thread and that Thread A runs first which automatically calls buzz method.

This would leave us in a complex situtation because Thread A would have to run n number of times before Thread B picks up. Sadly this is not we want to achieve. We want a situation where Thread A pauses after the first iteration permitting Thread B to run and then, Thread C picks up after Thread B's first iteration and same for Thread D. In other words, I want to keep all threads in sync.

To achieve this, we can use a threading object called barrier
Think of barrier as a delay that interupts a thread from continuing it's operation giving room to the next thread to carry out it's function and any other threads before the initial thread picks up from where it's last stop.

Code

from threading import Barrier

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

    # printFizz() outputs "fizz"
    def fizz(self, printFizz: 'Callable[[], None]') -> None:

        for i in range(1,self.n+1):
            if i%5!=0 and i%3==0:
                printFizz()
            self.block.wait()

    # printBuzz() outputs "buzz"
    def buzz(self, printBuzz: 'Callable[[], None]') -> None:

        for i in range(1,self.n+1):
            if i%3!=0 and i %5 ==0:
                printBuzz()
            self.block.wait()


    # printFizzBuzz() outputs "fizzbuzz"
    def fizzbuzz(self, printFizzBuzz: 'Callable[[], None]') -> None:

        for i in range(1,self.n+1):
            if i%5==0 and i%3==0:
                printFizzBuzz()
            self.block.wait()

    # printNumber(x) outputs "x", where x is an integer.
    def number(self, printNumber: 'Callable[[int], None]') -> None:

        for i in range(1,self.n+1):

            if (i % 3) !=0 and (i%5) !=0:
                printNumber(i)
            self.block.wait()

Enter fullscreen mode Exit fullscreen mode

Understanding the Code

  • Initialize barrier to 4, making it understand that 4 threads would be in sync. This would mean all 4 thread must hit the wait
self.block = Barrier(4)
Enter fullscreen mode Exit fullscreen mode
  • Starting our Program, Thread A calls Buzz method, The loop begins from 1 then our if statement is skipped because the condition is false which leads to the next line of code self.bar.wait(). This prevents buzz method from running next iteration and giving control to next thread which is Thread B.
# printBuzz() outputs "buzz"
    def buzz(self, printBuzz: 'Callable[[], None]') -> None:

        for i in range(1,self.n+1):
            if i%3!=0 and i %5 ==0:
                printBuzz()
            self.block.wait()
Enter fullscreen mode Exit fullscreen mode

The advantage of this approach is that all threads would remain blocked when they call wait()until the barrier reaches the specified number of threads waiting to be blocked before they get unblocked.

Oldest comments (0)