【问题标题】:Race condition with a simple channel in Go?Go 中有一个简单通道的竞争条件?
【发布时间】:2013-12-25 10:32:22
【问题描述】:

我是 Go 新手,但对于在 Linux 上运行 Go 版本 1.2 的一小段代码似乎有点罕见的竞争条件感到困惑。

基本上,我为int 创建一个通道,启动一个 go 例程从通道读取,然后将一个 int 写入通道。

package main

import "fmt"

func main() {
    channel := make(chan int)

    go func() {
        number := <- channel
        fmt.Printf("GOT IT: %d\n", number)
    }()

    fmt.Println("[+] putting num on channel")
    channel <- 42
    fmt.Println("[-] putting num on channel")
}

大约 90% 的时间输出符合预期:

$ go run test.go 
[+] putting num on channel
GOT IT: 42
[-] putting num on channel

然而,大约 10% 的时间,go 例程根本不会从通道中读取数字并且什么也不打印:

$ go run test.go 
[+] putting num on channel
[-] putting num on channel

我很困惑,因为这段代码与https://gobyexample.com/channels 的示例非常相似(我没有这个问题),只是我在 go 例程中从通道读取而不是写入通道.

我是否对渠道的运作方式存在根本性的误解,还是这里有其他原因?

【问题讨论】:

    标签: go channel


    【解决方案1】:

    你应该等到你的 goroutine 执行,然后你的,例如,你可以用sync.WaitGroup

    package main
    
    import (
      "fmt"
      "sync"
    )
    
    func main() {
      var wg sync.WaitGroup
    
      channel := make(chan int)
      wg.Add(1)
    
      go func() {
        number := <-channel
        fmt.Printf("GOT IT: %d\n", number)
        wg.Done()
      }()
    
      fmt.Println("[+] putting num on channel")
      channel <- 42
      wg.Wait()
      fmt.Println("[-] putting num on channel")
    }
    

    (goplay:http://play.golang.org/p/VycxTw_4vu)

    您也可以使用“通知渠道”来执行此操作,这表明工作已完成:

    package main
    
    import "fmt"
    
    func main() {
      channel := make(chan int)
      done := make(chan bool)
    
      go func() {
        number := <-channel
        fmt.Printf("GOT IT: %d\n", number)
        done <- true
      }()
    
      fmt.Println("[+] putting num on channel")
      channel <- 42
      <-done
      fmt.Println("[-] putting num on channel")
    }
    

    (goplay:http://play.golang.org/p/fApWQgtr4D)

    【讨论】:

      【解决方案2】:

      你有两个 goroutine,一个在 main() 中(隐式是一个 goroutine),一个是匿名的。

      它们通过同步通道进行通信,因此在通道通信之后,可以保证它们是同步的。

      此时,main() goroutine 中剩下的代码如下所示:

      fmt.Println("[-] putting num on a channel")
      

      匿名 goroutine 中留下的代码如下所示:

      fmt.Println("GOT IT: %d\n", number)
      

      现在你正在比赛:这些Printlns 的输出可能以任意顺序出现,甚至混合出现。当来自 main 的 Println() 完成时,该 goroutine 上将发生的下一件事就是您的程序将停止。这可能会阻止匿名 goroutine 中的部分或全部 Println 出现。

      【讨论】:

      • 最佳答案解释为什么它不工作,而不是提出一个可行的解决方案......
      【解决方案3】:

      您似乎希望接收 goroutine 在第二个 fmt.Println 执行之前运行完成。不保证会出现这种情况。如果程序终止,goroutines 不能保证到达其函数的末尾。

      当你看到没有显示“GOT IT”消息的输出时,通道传递了它的消息,但是 main 函数在 goroutine 之前完成了。程序终止,goroutine 再也没有机会调用fmt.Printf

      在您引用的示例中,main 函数以此结尾:

      go func() { messages <- "ping" }()
      msg := <-messages
      fmt.Println(msg)
      

      由于main 函数阻塞直到它接收一条消息,所以在这个例子中goroutine 总是运行到完成。在您的代码中,您的 goroutine 在从通道接收后执行一个步骤,并且未定义 goroutine 或 main 函数将在接收后执行下一行。

      【讨论】:

        猜你喜欢
        • 2018-05-22
        • 1970-01-01
        • 2023-03-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多