我一开始认为「这题没什么难的嘛~还不就那些套路再用一次!」,所以最早的实现版本,是写了一个中心控管的 goroutine,判断整除条件后,再把输出任务透过 channel 发派给其他 goroutine A, B, C, D。
直到我为了分享这题,将英文题目翻译为中文的时候,才发现自己误解题目了(尴尬)!题目真正的要求更困难,要各个 goroutine 自行负担检查整除条件的责任。所以只好重写 XD
但前面几题的解法,大多是把判断责任中心化,方便控管顺序。这次,与前面几题不同的是,这一题要求把判断责任分散到 thread A, B, C 中,所以每个 goroutine 也无法准确得知下一个要接棒的 goroutine 是哪一个?这样的顺序控制会由于分散化,变得更加困难。
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
type FizzBuzz struct {
n int
wg *sync.WaitGroup
streamBaton chan int
}
func (this *FizzBuzz) PrintLoop(passCondition func(int) bool, printString func(int)) {
defer this.wg.Done()
for i := 0; i <= this.n; i++ {
if passCondition(i) {
nextNum := <-this.streamBaton //接棒
if i == nextNum {
printString(i)
this.streamBaton <- i + 1 //交棒
} else {
this.streamBaton <- nextNum //把數字還回去
i--
}
runtime.Gosched()
}
}
}
func (this *FizzBuzz) PrintFizz() {
PassCondition := func(i int) bool { return (0 == i%3) && (0 != i%5) }
PrintString := func(i int) { fmt.Printf("Fizz(%d), ", i) }
this.PrintLoop(PassCondition, PrintString)
}
func (this *FizzBuzz) PrintBuzz() {
PassCondition := func(i int) bool { return (0 != i%3) && (0 == i%5) }
PrintString := func(i int) { fmt.Printf("Buzz(%d), ", i) }
this.PrintLoop(PassCondition, PrintString)
}
func (this *FizzBuzz) PrintFizzBuzz() {
PassCondition := func(i int) bool { return 0 == i%(3*5) }
PrintString := func(i int) { fmt.Printf("FizzBuzz(%d), ", i) }
this.PrintLoop(PassCondition, PrintString)
}
func (this *FizzBuzz) PrintNumber() {
PassCondition := func(i int) bool { return (0 != i%3) && (0 != i%5) }
PrintString := func(i int) { fmt.Printf("%d, ", i) }
this.PrintLoop(PassCondition, PrintString)
}
func main() {
start := time.Now()
for testCase := 0; testCase <= 20; testCase++ {
fizzbuzz := &FizzBuzz{
n: testCase,
wg: &sync.WaitGroup{},
streamBaton: make(chan int, 1),
}
fizzbuzz.wg.Add(4)
go fizzbuzz.PrintFizz()
go fizzbuzz.PrintBuzz()
go fizzbuzz.PrintFizzBuzz()
go fizzbuzz.PrintNumber()
fizzbuzz.streamBaton <- 0 //啟動交棒
fizzbuzz.wg.Wait()
close(fizzbuzz.streamBaton)
fmt.Println() //這個 Test Case 結束了,換行。
}
spentTime := time.Now().Sub(start)
fmt.Println("Spent time:", spentTime)
}