【问题标题】:Why there is no error that receiver is blocked?为什么没有接收器被阻塞的错误?
【发布时间】:2020-05-02 03:21:33
【问题描述】:

根据Go documentation:

接收者总是阻塞,直到有数据要接收

这个测试应该失败,因为对于来自通道的最后接收操作没有相应的写入:

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

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

【问题讨论】:

  • "这个测试应该失败,因为从一个通道的最后接收操作没有相应的写入",为什么测试会失败?确实“没有相应的写入”,但测试确实没有来检查通道上的任何尝试接收是否已完成。
  • 在我看来,至少运行时可以告诉用户有多少 goroutine 在退出程序时终止而没有接收到数据。 IE。我期待信息性消息。我认为它会增加价值
  • 很遗憾,您的意见与 Go 的工作方式不符。

标签: go channel goroutine


【解决方案1】:

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

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

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

在某些情况下,即使一个 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() 函数不会等待其他 goroutine 结束,因此它们可能在调用延迟函数时仍然处于活动状态,因此它们可能会或可能不会包含在剩余的 goroutines 中数数。如果你会使用例如sync.WaitGroup 等待他们结束,那么他们肯定不会被包括在内。示例见Prevent the main() function from terminating before goroutines finish in Golang。

【讨论】:

  • 但是 READ goroutine 必须被阻塞,因为 1 个写入丢失。但是仍然没有任何错误表明有关阻塞的读取程序
  • @AgniusVasiliauskas 是的,一个 goroutine 被阻塞直到应用程序结束,那又怎样?这不是错误,也不是死锁。
  • 我把死锁改成了“错误”。这不是重点。关键是为什么没有一些 goroutine 被阻塞的错误/信息消息。在我看来,这个程序是无效的,至少必须生成一些关于被阻止的例程的消息。为什么不是这样?
  • 多个 goroutine 被阻塞是正常的。 Go 不会尝试预测未来:goroutine 可能会在一小时、一天或一年内解除阻塞。此外,此时main() 退出,因此fmt.Println(3) 和死锁检测器都不能保证执行。
  • @AgniusVasiliauskas 不,它没有。它只是表明它等待的条件在程序的生命周期中从未发生过。 goroutine 可能正在等待来自用户的事件(例如ctrl+break 键),但用户可能永远不会按下这些键,并且应用程序可能会正常结束。
猜你喜欢
  • 2014-08-11
  • 1970-01-01
  • 2011-04-10
  • 1970-01-01
  • 2017-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-26
相关资源
最近更新 更多