go - 为什么没有接收器被阻塞的错误?

标签 go channel goroutine

根据Go documentation :

Receivers always block until there is data to receive



此测试应该失败,因为对于来自 channel 的最后接收操作,没有相应的 write :
package main

import "fmt"

func main() {
    c := make(chan int)    
    for i := 0; i < 4; i++ { // 4 async reads
      go func() {
            fmt.Println("received:", <-c)
         }()

    }    
    // just 3 writes, 1 write is missing
    c <- 1   
    c <- 2 
    c <- 3   
}

但是脚本在读取 goroutine 中没有失败并显示错误消息,但它成功打印了 3 个值:

收到:1
收到:2
收到:3

为什么会这样,或者我对同步有误解?

最佳答案

这里没有死锁,因为 main goroutine 没有被阻塞。它在 c 上发送 3 个值成功是因为有 4 个已启动的 goroutine 从它接收,然后它结束。并且它也会结束您的应用程序,它不会等待其他非 main goroutines 结束。见 No output from goroutine .

死锁意味着所有 goroutine 都被阻塞。这不是这里的情况。

尝试从没有人(当前或曾经)准备发送的 channel 接收不是错误。如果是事实,那是完全正常的。这是 channel 的用例之一:它充当同步工具,您可以发送/接收,并且操作将阻塞,直到另一端也准备好。

在某些情况下,甚至在整个应用程序生命周期中阻塞的 goroutine 也是正常的,例如goroutine 可能会等待用户输入,例如 CTRL+BREAK,用户可能永远不会按下它,应用程序可能会正常结束。

所以这不被认为是错误,并且不会为这些打印错误或警告消息。但是如果你很好奇,它很容易实现。只需将延迟函数添加到您的 main()这将在您的 main() 之前被称为最后一件事功能(以及您的应用程序)结束。在该打印中运行的 goroutine 的数量:

func main() {
    defer func() {
        fmt.Println("Remaining goroutines:", runtime.NumGoroutine()-1) //-1 for main
    }()

    // your code
}

加上这个,输出将是:
received: 1
received: 2
received: 3
Remaining goroutines: 1

如果您将循环更改为启动 14 个 goroutine 而不是 1 个,输出将显示剩余 11 个 goroutine。

最后一点:因为在您的应用程序中,main()函数不会等待其他 goroutines 结束,它们可能在调用延迟函数时仍然处于事件状态,因此它们可能会或可能不会包含在剩余的 goroutines 计数中。如果你会使用例如sync.WaitGroup等他们结束,那么他们肯定不会被包括在内。见 Prevent the main() function from terminating before goroutines finish in Golang例如。

关于go - 为什么没有接收器被阻塞的错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59750056/

相关文章:

json - 将多个 JSON 类型解析为同一个结构体

stack - 有什么方法可以让 Go 的 channel 表现得像一个堆栈

go - 从调用另一个 goroutine 的 goroutine 返回

for-loop - 从 3 个单独的 slice (每个结构属性一个 slice )创建新的组合结构 slice 时索引超出范围

arrays - 在Golang中,为什么这样的类型转换会导致运行时错误: index out of range?

function - 从函数返回函数以及为什么省略括号?

go - 我在尝试使用 GO 同时解析站点时遇到多个错误

c# - 等待来自永无止境的任务的事件异步

go - 了解从 channel 读取的行为

go - 双 channel 死锁