By Chee Leong
I’m trying to show the common pattern of multiplexing via gochannel and goroutine.
Definition
Fizz buzz is a group word game for children to teach them about division.[1] Players take turns to count incrementally, replacing any number divisible by three with the word "fizz", and any number divisible by five with the word "buzz".
Attention! Go Playground operation is single threaded hence output is always in order of the input. Try the code out at your computer for multithread goodness.
Basic
Let’s start with the basic example,
As demonstrated above, each loop contains blocking operation of processing and output.
Goroutine
To start applying goroutine, the inner loop operations are moved to a separate function.
To invoke a goroutine, simply append a go
keyword in front of the function call.
As you can see, we rely on a blocking Sleep
operation to stall the main
from exiting too early. However this is not good idea because we have no idea how long it takes for each processes to finish running.
Synchronization
Synchronization via WorkGroup
Although it is possible to do job waiting with channel, it is recommended to use WaitGroup
Now, we made sure all goroutine executed before exit.
Even though the current code looks good enough, fmt.Println
is actually a io operation.
If the write buffer is big enough, you will notice the output is contaminated, ie part of line 1 will have fragment of line 3.
To avoid undesirable side effect, we’ll apply multiplexing.
Multiplex
Unfortunately, I won’t be covering channel here. For the uninitiated, it is a pipe.
Read more about channel here.
The diagram of multiplexing we’re going to apply here
inputChan | 0 | ouputChan
main ========> fizzbuzzWorker | 1 | ========> printWorker
| 2 |
| 3 |
The changes for this implement might be a bit drastic
As you can see, the last part is declared first, aka in our example the printWorker
and outputChan
.
This is to assure the worker is consuming the piped message as soon as possible.
Since printWorker
is only a single process, we’ll be using a channel to signal the conclusion of the goroutine.
printWorker
listen to incoming string
message and print them out on the terminal.
Then, we starts n (system core) amount of fizzbuzzWorker
goroutine.
fizzbuzzWorker
ingests int
input, and ouput string
Now, with all the worker set, we start to feed inputChan
. Once it’s finished, we closed inputChan
so our fizzbuzzWorker
knows when there’s no more job and exit.
Warning, failed to close a channel will result in a deadlock, the best case is the compiler or runtime throws panic immediately, worst case is the program stucked but still run indefinitely.
Next, the program waits for all n
of fizzbuzzWorker
to finish their task, then we’ll close outputChan
so printWorker
knows when to stop.
Once, printWorker
finished its job, the printDone
is signalled and program is now exited happily.