为了账号安全,请及时绑定邮箱和手机立即绑定

为什么我的代码中没有这个缓冲通道块?

为什么我的代码中没有这个缓冲通道块?

Go
白衣染霜花 2022-05-10 16:16:29
我正在学习 Go 语言。有人可以在这里解释输出吗?package mainimport "fmt"var c = make(chan int, 1)func f() {    c <- 1    fmt.Println("In f()")}func main() {    go f()    c <- 2    fmt.Println(<-c)    fmt.Println(<-c)}输出:In f()21Process finished with exit code 0为什么“In f()”出现在“2”之前?如果在“2”之前打印“In f()”,则缓冲通道应该阻塞。但这并没有发生,为什么?
查看完整描述

2 回答

?
幕布斯7119047

TA贡献1794条经验 获得超8个赞

导致此事件发生的事件顺序如下:

  1. 触发 goroutine。

  2. 写入2频道。通道的容量现已耗尽。

  3. 从通道中读取并输出结果。

  4. 写入1频道。

  5. 从通道中读取并输出结果。

导致死锁的事件顺序如下:

  1. 触发 goroutine。

  2. 写入1频道。通道的容量现已耗尽。

  3. 写入2频道。由于通道的缓冲区已满,因此阻塞。

您提供的输出似乎表明goroutine首先完成并且程序没有死锁,这与上述两种情况的解释相矛盾。这是发生的事情:

  1. 触发 goroutine。

  2. 写入2频道。

  3. 2从频道读取。

  4. 写入1频道。

  5. 输出In f()

  6. 输出2从通道接收到的数据。

  7. 1从频道读取。

  8. 输出1从通道接收到的数据。

请记住,除非您以编程方式强制执行它们,否则您对 goroutine 的调度没有任何保证。当您启动一个 goroutine 时,该 goroutine 的第一个代码实际执行的时间以及在此之前启动代码的进度是未定义的。请注意,由于您的代码依赖于特定的事件顺序,因此它被该定义破坏,只是明确地说。

此外,在程序中的任何时候,调度程序都可以决定在不同的 goroutine 之间切换。甚至单行也fmt.Printtln(<-c)包含多个步骤,并且在每个步骤之间都可能发生切换。


查看完整回答
反对 回复 2022-05-10
?
慕哥9229398

TA贡献1877条经验 获得超6个赞

要重现块,您必须多次运行该代码,这是最简单的测试方法。你没有运气的障碍。但不能保证:


var c = make(chan int, 1)


func f() {


    c <- 1


    fmt.Println("In f()")

}


func TestF(t *testing.T) {

    go f()


    c <- 2

    fmt.Println(<-c)

    fmt.Println(<-c)


}

并使用命令运行:


go test -race -count=1000 -run=TestF -timeout=4s

测试次数在哪里count。它再现了对我的阻塞。提供超时以不等待默认 10 分钟


查看完整回答
反对 回复 2022-05-10
  • 2 回答
  • 0 关注
  • 107 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信