• 一个Golang例子:for + goroutine + channel


    Rob Pike 在 Google I/O 2012 - Go Concurrency Patterns 里演示了一个例子(daisy chain)。 视频地址:https://www.youtube.com/watch?v=f6kdp27TYZs

    这个例子抽象于“传话游戏”,几个人站成一队,第一个人跟第二个人悄悄说一句话,依次传到最后一个人,看看最后一个人听到的和第一个人说的差别有多大。

    代码如下:

    package main
    
    import "fmt"
    
    func pass(left, right chan int){
        left <- 1 + <- right
    }
    
    func main(){
        const n = 50
        leftmost := make(chan int)
        right := leftmost
        left := leftmost
    
        for i := 0; i< n; i++ {
            right = make(chan int)
            // the chain is constructed from the end
            go pass(left, right) // the first goroutine holds (leftmost, new chan)
            left = right         // the second and following goroutines hold (last right chan, new chan)
        }
        go func(c chan int){ c <- 1}(right)
        fmt.Println("sum:", <- leftmost)
    }

    这段代码产生了一个单向的管道环,每个节点对输入的值加了1,然后输出给下一个节点,最后到终点 leftmost。重点我认为有以下几个:

    1,循环中的 goroutine ;

    2,unbuffered channel 的连接和阻塞;

    3,goroutine 对 channel 的竞争;

    第一点:循环中的 goroutine 其实很像 js 中的循环中的异步请求,或者更直观的,像是循环中的 setTimeout()。对于 main 来说,goroutine 是异步的,是对线程的细粒度抽象,把它当做一个异步任务就可以了。但是包含了 channel 的 goroutine 就有了阻塞的成分。channel 也体现了 Golang 的设计理念之一:Do not communicate by sharing memory; instead, share memory by communicating 。

    第二点:unbuffered channel (make(chan int)) 可以看做是非常短的管子,里面连一个字节都不能存储,必须先找到两端的输入和输出,不然就会出问题(阻塞)。比如下面代码:

    func main(){
        c := make(chan int)
        c <- 1
        fmt.Println( <- c)
    }

    // fatal error: all goroutines are asleep - deadlock!

    上面的代码中, c <- 1 这一行给 channel 输入了数据,但是此时还没有接收者(代码是同步执行的),因此卡死在这儿了。

    那如果先给 channel 指定了输出,然后再输入数据呢? 结果是一样的,只有接收者没有输入者,一样卡死。

    改成这样就可以了:

    func main(){
        c := make(chan int)
        go func(){fmt.Println(<- c)}()
        c <- 1
    }

    这里先在 goroutine 里指定了 Println 作为接收者,然后给了输入。

    可以理解为:channel 不能同时输入和输出, <- c <- 1  会报错(可能 Golang 觉得这样是没有意义的);指定输入和输出必须写在两行,而代码的同步执行决定了不能同时指定输入和输出,因此只能用 goroutine 。实际上 channel 本身也是为了 goroutine 间的通讯。

    buffered channel 就比较好理解,是带有容器的管道,可以存储一定数量的数据。但是当容器满的时候,表现就和 unbuffered channel 一样,会阻塞。

    第三点:第一块代码里产生的管道环是从终点开始连接起的,最后一根管道实际上是数据流的第一节管道。在这个环刚完成的时候,所有管道都是空的,没有输入。这时所有的 goroutine 都被阻塞了,倒数第三行的 go 给了第一个管道一个输入,于是这点数据就流到了最后。

  • 相关阅读:
    21.Merge Two Sorted Lists 、23. Merge k Sorted Lists
    34. Find First and Last Position of Element in Sorted Array
    leetcode 20. Valid Parentheses 、32. Longest Valid Parentheses 、301. Remove Invalid Parentheses
    31. Next Permutation
    17. Letter Combinations of a Phone Number
    android 常见分辨率(mdpi、hdpi 、xhdpi、xxhdpi )及屏幕适配注意事项
    oc 异常处理
    oc 类型判断
    oc Delegate
    oc 协议
  • 原文地址:https://www.cnblogs.com/jasonxuli/p/6861791.html
Copyright © 2020-2023  润新知