multithreading - 如何安全地与 Golang 中的 goroutine 中的 channel 交互

标签 multithreading go range goroutine channel

我是新手,我正在尝试了解 goroutine 中 channel 的工作方式。据我了解,关键字range可用于迭代 channel 的值,直到 channel 关闭或缓冲区耗尽为止;因此,for range c 将重复循环,直到缓冲区耗尽。

我有以下简单的函数可以为 channel 增加值(value):

func main() {

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

}

我有两个 printchannel 实现,我不确定为什么行为不同。

实现1:

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

输出:1 3 5 7

实现2:

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

输出:0 1 2 3 4 5 6 7 8

我并没有期待这些输出!

想要的输出:0 1 2 3 4 5 6 7 8 9

main 函数和 printchannel 函数是否应该在两个线程上并行运行,一个向 channel 添加值,另一个读取值直到 channel 关闭?我可能在这里遗漏了一些基本的 go/thread 概念,指向它的指针会很有帮助。

非常感谢对此的反馈(以及我对 goroutine 中 channel 操作的理解)!

最佳答案

实现 1. 您从 channel 中读取两次 - range c<-c都在从 channel 中读取内容。

实现2。这是正确的方法。您可能看不到 9 打印的原因是两个 goroutine 可能在并行线程中运行。在这种情况下,它可能会像这样:

  1. 主协程将 9 发送到 channel 并阻塞直到被读取
  2. 第二个 goroutine 从 channel 接收到 9
  3. 主协程解除阻塞并退出。这会终止整个程序,不会给第二个 goroutine 机会打印 9

在这种情况下,你必须同步你的 goroutine。例如,像这样

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
}

关于 goroutine 与线程。您不应该混淆它们,并且可能应该理解它们之间的区别。 Goroutines 是绿色线程。关于该主题有无数的博客文章、讲座和 stackoverflow 答案。

关于multithreading - 如何安全地与 Golang 中的 goroutine 中的 channel 交互,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49971237/

相关文章:

java - 'correct' 测试 JUnit 测试无法直接看到的 Runnables 的方法是什么?

firebase - GoLang Admin SDK上的Firebase电话号码验证

range - 如何形成字段必须以某些字母开头的 Endeca 查询

c# - 哪种算法用于在数字范围内查找空数字范围?

go - 获取未初始化 slice 的类型

ruby - Ruby 中的 Æ、Ø 和 Å - 为什么 ("A"。 ."Å").to_a 返回双字母表?

java - 我的代码线程安全吗?

java - 执行者取消未决任务 - 需要帮助

c++ - 我如何使用c/c++实时录制和播放我的声音

pointers - 下面的代码如何实现接口(interface)?