为什么接收器没有错误被阻止?

问题描述 投票:-1回答:1

根据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

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

go channel goroutine
1个回答
4
投票

这里没有死锁,因为main goroutine没有被阻塞。它在c上发送3个值,这是成功的,因为有4个启动的goroutine从那里接收,然后结束。有了它,您的应用程序也结束了,它不会等待其他非main goroutine结束。参见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结束,因此在调用延迟函数时它们可能仍处于活动状态,因此它们可能会或可能不会包含在剩余的goroutine计数中。如果您使用例如sync.WaitGroup等待它们结束,那么它们肯定不会包含在内。有关示例,请参见Prevent the main() function from terminating before goroutines finish in Golang

© www.soinside.com 2019 - 2024. All rights reserved.