Multi-Threaded FizzBuzz
Tags: python, algorithms, thread, classes
Published: 2022-09-26 13:10:40

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"]
```
Example 2:

```
Input: n = 5
Output: [1,2,"fizz",4,"buzz"]
```

**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()"
```
 
**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](https://realpython.com/intro-to-python-threading/#threading-objects) 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()

```
**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)
```

- 
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()
```

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.