doutan1970 2018-04-22 22:13
浏览 98
已采纳

如何在Golang中与goroutines中的通道安全交互

I am new to go and I am trying to understand the way channels in goroutines work. To my understanding, the keyword range could be used to iterate over a the values of the channel up until the channel is closed or the buffer runs out; hence, a for range c will repeatedly loops until the buffer runs out.

I have the following simple function that adds value to a channel:

func main() {

    c := make(chan int)
    go printchannel(c)
    for i:=0; i<10 ; i++ {
        c <- i
    }

}

I have two implementations of printchannel and I am not sure why the behaviour is different.

Implementation 1:

func printchannel(c chan int) {
    for range c {
        fmt.Println(<-c)
    }
}

output: 1 3 5 7

Implementation 2:

func printchannel(c chan int) {
    for i:=range c {
        fmt.Println(i)
    }
}

output: 0 1 2 3 4 5 6 7 8

And I was expecting neither of those outputs!

Wanted output: 0 1 2 3 4 5 6 7 8 9

Shouldnt the main function and the printchannel function run on two threads in parallel, one adding values to the channel and the other reading the values up until the channel is closed? I might be missing some fundamental go/thread concept here and pointers to that would be helpful.

Feedback on this (and my understanding to channels manipulation in goroutines) is greatly appreciated!

  • 写回答

4条回答 默认 最新

  • dongxi5423 2018-04-22 23:44
    关注

    Implementation 1. You're reading from the channel twice - range c and <-c are both reading from the channel.

    Implementation 2. That's the correct approach. The reason you might not see 9 printed is that two goroutines might run in parallel threads. In that case it might go like this:

    1. main goroutine sends 9 to the channel and blocks until it's read
    2. second goroutine receives 9 from the channel
    3. main goroutine unblocks and exits. That terminates whole program which doesn't give second goroutine a chance to print 9

    In case like that you have to synchronize your goroutines. For example, like so

    func printchannel(c chan int, wg *sync.WaitGroup) {
        for i:=range c {
            fmt.Println(i)
        }
    
        wg.Done() //notify that we're done here
    }
    
    func main() {
        c := make(chan int)
        wg := sync.WaitGroup{}
    
        wg.Add(1) //increase by one to wait for one goroutine to finish
                  //very important to do it here and not in the goroutine
                  //otherwise you get race condition
    
        go printchannel(c, &wg) //very important to pass wg by reference
                                //sync.WaitGroup is a structure, passing it
                                //by value would produce incorrect results
    
        for i:=0; i<10 ; i++ {
            c <- i
        }
    
        close(c)  //close the channel to terminate the range loop
        wg.Wait() //wait for the goroutine to finish
    }
    

    As to goroutines vs threads. You shouldn't confuse them and probably should understand the difference between them. Goroutines are green threads. There're countless blog posts, lectures and stackoverflow answers on that topic.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(3条)

报告相同问题?

悬赏问题

  • ¥15 微信小程序协议怎么写
  • ¥15 c语言怎么用printf(“\b \b”)与getch()实现黑框里写入与删除?
  • ¥20 怎么用dlib库的算法识别小麦病虫害
  • ¥15 华为ensp模拟器中S5700交换机在配置过程中老是反复重启
  • ¥15 java写代码遇到问题,求帮助
  • ¥15 uniapp uview http 如何实现统一的请求异常信息提示?
  • ¥15 有了解d3和topogram.js库的吗?有偿请教
  • ¥100 任意维数的K均值聚类
  • ¥15 stamps做sbas-insar,时序沉降图怎么画
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看