根据Go文档:
接收者总是阻塞直到有数据要接收
该测试应该失败,因为对于最后一次从通道接收操作,没有相应的写入:
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没有被阻塞。它发送3个c
成功的值,因为有4个已启动的goroutine从那里接收,然后结束。有了它,您的应用程序也将结束,它不会等待其他非main
goroutine结束。请参阅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计数中。如果您使用eg sync.WaitGroup
等它们结束,那么它们肯定不会包含在内。有关示例,请参见防止main()函数在Goroutine在Golang中完成之前终止。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句