【问题标题】:Unbuffered channel in GoGo中的无缓冲通道
【发布时间】:2018-10-02 03:29:48
【问题描述】:

这是一个关于无缓冲通道的简单示例代码:

ch01 := make(chan string)

go func() {
    fmt.Println("We are in the sub goroutine")
    fmt.Println(<-ch01)
}()

fmt.Println("We are in the main goroutine")
ch01 <- "Hello"

我得到的结果:

We are in the main goroutine 
We are in the sub goroutine 
Hello

去游乐场: https://play.golang.org/p/rFWQbwXRzGw

据我了解,发送操作阻塞了主协程,直到子协程在通道ch01 上执行接收操作。然后程序就退出了。

在发送操作之后放置 sub goroutine 之后:

fmt.Println("We are in the main goroutine")
ch01 <- "Hello"

go func() {
    fmt.Println("We are in the sub goroutine")
    fmt.Println(<-ch01)
}()

发生了死锁:

We are in the main goroutine
fatal error: all goroutines are asleep - deadlock!

去游乐场 https://play.golang.org/p/DmRUiBG4UmZ

这次发生了什么?这是否意味着在ch01 &lt;- "Hello" 之后主 goroutine 立即被阻塞,以至于子 goroutine 没有机会运行?如果是真的,我应该如何理解第一个代码示例的结果?(首先在 main goroutine 中,然后在 sub goroutine 中)。

【问题讨论】:

    标签: go


    【解决方案1】:

    这是否意味着在 ch01

    这是真的。你明白写的东西。未指定生成的 goroutine 的评估顺序,只能使用同步工具(通道、互斥锁)进行控制。第一个示例中的子 goroutine 也可以在另一个环境中先打印()。它只是未指定

    【讨论】:

      【解决方案2】:

      对于无缓冲的通道,go 例程被阻塞,直到没有人接收它。首先应该有一个 go 例程从通道接收值,然后将值发送到通道。例如,当我们向通道发送值时,需要创建一个缓冲通道,以便将值保存到缓冲中,直到没有人接收它,这样就可以了。

      package main
      
      import (
          "fmt"
          "time"
      )
      
      func main() {
          ch01 := make(chan string, 10)
          ch01 <- "Hello"
          go func() {
              fmt.Println("We are in the sub goroutine")
              fmt.Println(<-ch01)
          }()
      
          fmt.Println("We are in the main goroutine")
          time.Sleep(1 * time.Second)
      }
      

      Playground

      【讨论】:

        【解决方案3】:

        首先,go-routines 并发运行。在第一个例子中,子goroutine已经启动,但是在第二个例子中,当发送操作出现时,go-routine还没有启动。

        逐行考虑。

        在第一个示例中,sub-goroutine 在发送操作出现在主 go-routine 之前同时启动。结果,当发送操作发生时,已经有一个接收者(sub-goroutine)存在。

        如果你调整第一个例子,

        package main
        
        import (
            "fmt"
            "time"
        )
        
        func main() {
            ch01 := make(chan string)
        
            go func() {
                fmt.Println("We are in the sub goroutine")
                fmt.Println(<-ch01)
            }()
        
            // wait for start of sub-routine
            time.Sleep(time.Second * 2)
        
            fmt.Println("We are in the main goroutine")
            ch01 <- "Hello"
        
            // wait for the routine to receive and print the string
            time.Sleep(time.Second * 2)
        
        }
        

        输出将是

        We are in the sub goroutine
        We are in the main goroutine
        Hello
        

        因此,您可以看到子 goroutine 已经启动。它正在等待在通道上接收。当主协程在通道中发送字符串时,子协程恢复并接收信号。

        但是在第二个例子中,程序卡在了 main goroutine send operation,而 sub goroutine 还没有启动也不会启动,因为程序还没有得到那一行。所以没有其他接收器接收信号。所以程序陷入了死锁。

        【讨论】:

          【解决方案4】:

          一个无缓冲的通道在发送时阻塞,直到接收器准备好读取。在您的第一个示例中,首先设置了一个阅读器,因此当发送发生时,它可以立即发送。

          在您的第二个示例中,发送发生在接收器准备好之前,因此发送阻塞并且程序死锁。

          您可以通过创建缓冲通道来修复第二个示例,但是您可能永远看不到 goroutine 的输出,因为程序可能会在输出缓冲区被刷新之前退出(主 goroutine)。在调度之前,goroutine 甚至可能不会作为主出口运行。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-10-17
            • 2021-12-25
            • 1970-01-01
            • 2017-09-06
            • 2019-01-07
            • 2018-04-15
            • 2013-12-17
            • 2018-02-18
            相关资源
            最近更新 更多