go - 为什么这个缓冲的 channel 在我的代码中没有阻塞?

标签 go

我正在学习Go语言。有人可以在这里解释输出吗?

package main

import "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()
2
1

Process finished with exit code 0

为什么“In f()”出现在“2”之前?如果在“2”之前打印“In f()”,则缓冲的 channel 应阻塞。但这没有发生,为什么?

其他输出是合理的。

Image of my confusing result

最佳答案

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

  • 触发goroutine。
  • 2写入 channel 。 channel 的容量现已耗尽。
  • 从 channel 读取并输出结果。
  • 1写入 channel 。
  • 从 channel 读取并输出结果。

  • 导致此事件死锁的事件顺序如下:
  • 触发goroutine。
  • 1写入 channel 。 channel 的容量现已耗尽。
  • 2写入 channel 。由于 channel 的缓冲区已满,因此将阻塞。

  • 您提供的输出似乎表明goroutine首先完成,并且程序没有死锁,这与上述两种情况相矛盾。这是发生了什么:
  • 触发goroutine。
  • 2写入 channel 。
  • 从 channel 读取2
  • 1写入 channel 。
  • 输出In f()
  • 输出从 channel 接收的2
  • 从 channel 读取1
  • 输出从 channel 接收的1

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

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

    关于go - 为什么这个缓冲的 channel 在我的代码中没有阻塞?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59987856/

    相关文章:

    assembly - 如何获得一个简短的 Go 程序的汇编输出?

    multithreading - 使用 Go 1.2 的线程 CGO

    json - 如何清理golang中的输入数据?

    go - 有没有办法在 Go 中的任何导入之前设置环境变量?

    pointers - 在golang中使用指针进行多次分配和创建

    go - Jenkins GO运行失败,git 128

    go - ListObjectsV2 api 调用是否在 Google Cloud Storage 中实现?

    xml - 在 go 中解码 XML 时出现问题

    go - 使用 Apollo Client 和 graphql-go 的 Graphql 查询错误为 'Unknown type Int'

    json - json.RawMessage 编码(marshal)