Created
April 26, 2017 15:57
-
-
Save NoraCodes/433654da49c93c2da347bb7b5f7adc79 to your computer and use it in GitHub Desktop.
A parallel FizzBuzz using Goroutines.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"strconv" | |
) | |
// Return a channel which will produce a stream of numbers | |
// from minimum to maximum minus one. | |
func intRange(minimum int, maximum int) <-chan int { | |
// ch is a two-way unbuffered channel. When a write is performed, | |
// the writing Goroutine blocks until some other routine performs | |
// a read on the other end of the channel, and vice versa. | |
ch := make(chan int) | |
// Here, an anonymous function is spun off to perform the required work | |
go func() { | |
for i := minimum; i < maximum; i++ { | |
// Write the value to the channel and block until it is read | |
ch <- i | |
} | |
// The channel is no longer needed. Closing the channel will unblock any | |
// Goroutine trying to read from it. | |
close(ch) | |
}() // These parentheses call this anonymous function | |
return ch // Because of the type signature of the function, only | |
// the recieving end of the channel is actually returned. | |
} | |
// Perform the actual FizzBuzz calculation | |
func checkFizzBuzz(value int) string { | |
var s string | |
if value % 3 == 0 { | |
s += "Fizz" | |
} | |
if value % 5 == 0 { | |
s += "Buzz" | |
} | |
if s == "" { | |
s = strconv.Itoa(value) | |
} | |
return s | |
} | |
// Spin off a Goroutine which will compute the FizzBuzz calculation for the given value | |
// and return it on the given channel | |
func asyncCheckFizzBuzz(value int, ch chan string) { | |
go func() { | |
ch <- fmt.Sprintf("%s: %s", strconv.Itoa(value), checkFizzBuzz(value)); | |
}() | |
} | |
func main() { | |
// Create the two channels that will be used. One is bound to a goroutine that | |
// produces numbers in sequence from 1 to 99. This channel has a buffer size of | |
// zero. That means that when the Goroutine on the other end writes to it, | |
// that write blocks until the main Goroutine reads from it. | |
range_ch := intRange(1,100) | |
// The other is bound to nothing. It will be used to get the results from | |
// the async FizzBuzz computations. | |
// This channel has an arbitrarily chosen buffer size of 10, which allows | |
// writes to it without concurrent reads until the buffer fills. | |
// Play with this value and see how it changes the output ordering. | |
result_ch := make(chan string, 10) | |
// Receive from the number range generator and spin off Goroutines that | |
// will compute the FizzBuzz calculation. | |
for i := range range_ch { | |
asyncCheckFizzBuzz(i, result_ch) | |
} | |
// Now, receive values from the FizzBuzz Goroutines and print their results. | |
// In order to prevent a deadlock, it's necessary have to keep track of how many | |
// values have been computed so far and stop waiting when there are no more left. | |
var received int | |
for v := range result_ch { | |
received += 1 | |
fmt.Printf("%s\n", v) | |
if received == 99 { | |
break | |
} | |
} | |
// Since this channel was created by this main Goroutine, and no other | |
// Goroutine will close it, it needs to be closed here. This could be | |
// done thus: | |
// close(result_ch) | |
// However, this isn't necessary; the runtime will handle it automatically. | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment