• 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 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)
    }
  • 相关阅读:
    random、shutil、shevle、标准输入输出错误流
    Python中Json 和 pickle 详解
    Python 中的sys、os、os.path模块总结
    Python 中time,calendar,datatime模块总结
    包的概念和导入包的方法
    Python 中模块及其导入方式
    迭代器和生成器
    python 函数参数
    JAVA 配置Path环境变量
    腾讯云服务器简单配置web项目
  • 原文地址:https://www.cnblogs.com/smallleiit/p/12640550.html
Copyright © 2020-2023  润新知