Loading

Go语言精进之路读书笔记第31条——优先考虑并发设计

31.1 并发与并行

1.并行方案
在处理器核数充足的情况下启动多个单线程应用的实例

2.并发方案
重新做应用结构设计,即将应用分解成多个在基本执行单元(例如操作系统线程)中执行的、可能有一定关联关系的代码片段

goroutine:由Go运行时负责调度的用户层轻量级线程,相比传统操作系统线程而言具有如下优势
(1) 资源占用小,每个goroutine的初始栈大小仅为2KB,参考$GOROOT/src/runtime/stack.go::_StackMin
(2) 由Go运行时而不是操作系统调度,goroutine上下文切换代价较小
(3) 语言原生支持:goroutine由go关键字接函数或方法创建,函数或方法返回即表示goroutine退出
(4) 语言内置channel作为goroutine间通信原语,为并发设计提供强大支撑

31.2 Go并发设计实例

1.第一版:顺序设计

func idCheck() int {
    time.Sleep(time.Millisecond * time.Duration(idCheckTmCost))
    println("\tidCheck ok")
    return idCheckTmCost
}

func bodyCheck() int {
    time.Sleep(time.Millisecond * time.Duration(bodyCheckTmCost))
    println("\tbodyCheck ok")
    return bodyCheckTmCost
}

func xRayCheck() int {
    time.Sleep(time.Millisecond * time.Duration(xRayCheckTmCost))
    println("\txRayCheck ok")
    return xRayCheckTmCost
}

func airportSecurityCheck() int {
    println("airportSecurityCheck ...")
    total := 0

    total += idCheck()
    total += bodyCheck()
    total += xRayCheck()

    println("airportSecurityCheck ok")
    return total
}

func main() {
    total := 0
    passengers := 30
    for i := 0; i < passengers; i++ {
        total += airportSecurityCheck()
    }
    println("total time cost:", total)
}

30名乘客,只设置了一条安检通道,共耗时10800ms

2.第二版:并行方案

func idCheck(id int) int {
    time.Sleep(time.Millisecond * time.Duration(idCheckTmCost))
    print("\tgoroutine-", id, ": idCheck ok\n")
    return idCheckTmCost
}

func bodyCheck(id int) int {
    time.Sleep(time.Millisecond * time.Duration(bodyCheckTmCost))
    print("\tgoroutine-", id, ": bodyCheck ok\n")
    return bodyCheckTmCost
}

func xRayCheck(id int) int {
    time.Sleep(time.Millisecond * time.Duration(xRayCheckTmCost))
    print("\tgoroutine-", id, ": xRayCheck ok\n")
    return xRayCheckTmCost
}

func airportSecurityCheck(id int) int {
    print("goroutine-", id, ": airportSecurityCheck ...\n")
    total := 0

    total += idCheck(id)
    total += bodyCheck(id)
    total += xRayCheck(id)

    print("goroutine-", id, ": airportSecurityCheck ok\n")
    return total
}

func start(id int, f func(int) int, queue <-chan struct{}) <-chan int {
    c := make(chan int)
    go func() {
        total := 0
        for {
            _, ok := <-queue
            if !ok {
                c <- total
                return
            }
            total += f(id)
        }
    }()
    return c
}

func max(args ...int) int {
    n := 0
    for _, v := range args {
        if v > n {
            n = v
        }
    }
    return n
}

func main() {
    total := 0
    passengers := 30
    c := make(chan struct{})
    c1 := start(1, airportSecurityCheck, c)
    c2 := start(2, airportSecurityCheck, c)
    c3 := start(3, airportSecurityCheck, c)

    for i := 0; i < passengers; i++ {
        c <- struct{}{}
    }
    close(c)

    total = max(<-c1, <-c2, <-c3)
    println("total time cost:", total)
}

30名乘客,设置了三条安检通道,共耗时3600ms

3.第三版:并发方案

func idCheck(id string) int {
    time.Sleep(time.Millisecond * time.Duration(idCheckTmCost))
    print("\tgoroutine-", id, "-idCheck: idCheck ok\n")
    return idCheckTmCost
}

func bodyCheck(id string) int {
    time.Sleep(time.Millisecond * time.Duration(bodyCheckTmCost))
    print("\tgoroutine-", id, "-bodyCheck: bodyCheck ok\n")
    return bodyCheckTmCost
}

func xRayCheck(id string) int {
    time.Sleep(time.Millisecond * time.Duration(xRayCheckTmCost))
    print("\tgoroutine-", id, "-xRayCheck: xRayCheck ok\n")
    return xRayCheckTmCost
}

func start(id string, f func(string) int, next chan<- struct{}) (chan<- struct{}, chan<- struct{}, <-chan int) {
    queue := make(chan struct{}, 10)
    quit := make(chan struct{})
    result := make(chan int)

    go func() {
        total := 0
        for {
            select {
            case <-quit:
                result <- total
                return
            case v := <-queue:
                total += f(id)
                if next != nil {
                    next <- v
                }
            }
        }

    }()
    return queue, quit, result
}

func newAirportSecurityCheckChannel(id string, queue <-chan struct{}) {
    go func(id string) {
        print("goroutine-", id, ": airportSecurityCheckChannel is ready...\n")
        // start xRayCheck routine
        queue3, quit3, result3 := start(id, xRayCheck, nil)

        // start bodyCheck routine
        queue2, quit2, result2 := start(id, bodyCheck, queue3)

        // start idCheck routine
        queue1, quit1, result1 := start(id, idCheck, queue2)

        for {
            select {
            case v, ok := <-queue:
                if !ok {
                    close(quit1)
                    close(quit2)
                    close(quit3)
                    total := max(<-result1, <-result2, <-result3)
                    print("goroutine-", id, ": airportSecurityCheckChannel time cost:", total, "\n")
                    print("goroutine-", id, ": airportSecurityCheckChannel closed\n")
                    return
                }
                queue1 <- v
            }
        }
    }(id)
}

func max(args ...int) int {
    n := 0
    for _, v := range args {
        if v > n {
            n = v
        }
    }
    return n
}

func main() {
    passengers := 30
    queue := make(chan struct{}, 30)
    newAirportSecurityCheckChannel("channel1", queue)
    newAirportSecurityCheckChannel("channel2", queue)
    newAirportSecurityCheckChannel("channel3", queue)

    time.Sleep(5 * time.Second) // 保证上述三个goroutine都已经处于ready状态
    for i := 0; i < passengers; i++ {
        queue <- struct{}{}
    }
    time.Sleep(5 * time.Second)
    close(queue) // 为了打印各通道的处理时长
    time.Sleep(1000 * time.Second)
}

30名乘客,还是三条安检通道,但是增加了工作人员(计算资源),共耗时2160ms

posted @ 2024-02-21 18:58  brynchen  阅读(10)  评论(0编辑  收藏  举报