LeetCode Go 并发题详解:交替打印字符串
作者:互联网
原文地址:https://mp.weixin.qq.com/s/K032xlARjiyS8ecJrqZXaA
本题 LeetCode 链接:
https://leetcode.com/problems/fizz-buzz-multithreaded/
本题题目
给定一个数列从 1 ~ n,依序输出,但是:
- 如果 n 可以被 3 整除,输出 "fizz"
- 如果 n 可以被 5 整除,输出 "buzz"
- 如果 n 同时可以被 3 与 5 整除,输出 "fizzbuzz"
实战要求:使用 4 个执行线程实现一个多执行线程版本。一个 FizzBuzz 的 instance 要被传递到以下四个执行线程中:
- Thread A 会调用
fizz()
以检查 n 是否可以被 3 整除?若可以就输出 fizz - Thread B 会调用
buzz()
以检查 n 是否可以被 5 整除?若可以就输出 buzz - Thread C 会调用
fizzbuzz()
以检查 n 是否可以被 3, 5 整除?若可以就输出 fizzbuzz - Thread D 会调用
number()
照常输出原本数字 n
本题考核难点?判断责任去中心化!
我一开始认为「这题没什么难的嘛~还不就那些套路再用一次!」,所以最早的实现版本,是写了一个中心控管的 goroutine,判断整除条件后,再把输出任务透过 channel 发派给其他 goroutine A, B, C, D。
直到我为了分享这题,将英文题目翻译为中文的时候,才发现自己误解题目了(尴尬)!题目真正的要求更困难,要各个 goroutine 自行负担检查整除条件的责任。所以只好重写 XD
在过去的 LeetCode Concurrency 详解中,我提到过很多次:
goroutine 若不刻意控制,将无法保证执行的先后顺序,因此本题就是要考核对 goroutine 顺序控制的能力。
但前面几题的解法,大多是把判断责任中心化,方便控管顺序。这次,与前面几题不同的是,这一题要求把判断责任分散到 thread A, B, C 中,所以每个 goroutine 也无法准确得知下一个要接棒的 goroutine 是哪一个?这样的顺序控制会由于分散化,变得更加困难。
By the way,我还解过「DiningPhilosophers」这一题用的就是去中心化方法,但目前还没写那一题详解。
package mainimport ( "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) }
标签:PassCondition,int,PrintString,fizzbuzz,详解,func,Go,LeetCode,FizzBuzz 来源: https://www.cnblogs.com/smallleiit/p/12640550.html