3 回答
TA贡献1851条经验 获得超5个赞
您有两个功能,它们需要同时运行才能使通道通信正常工作 - 一个必须同时接收另一个发送。在这种情况下:
go pp(c,quit) fibonacci(c, quit)
你pp
从一个 goroutine 开始,它开始运行,然后你调用fibonacci
,这样两者都在运行,一切正常。如果按照您的建议将其更改为:
fibonacci(c, quit) go pp(c,quit)
然后你作为一个常规函数调用,而不是作为一个 goroutine,这意味着下一行在返回fibonacci
之前不会被执行。fibonacci
因为fibonacci
期望从它的通道接收到一些东西,所以它会阻塞直到那发生——这是永远不会发生的,因为没有任何东西同时从它读取。因此你的僵局。
问题不在于函数的顺序或通道缓冲——问题在于如果你想同时运行两个函数,你首先调用的那个必须作为 goroutine 运行(或两者):
go fibonacci(c, quit) pp(c,quit)
可以正常工作,因为它fibonacci
同时调用,然后pp
可以同时运行的调用。
如果您使用的是 a WaitGroup
,您甚至可以将它们都作为 goroutines 运行,并且它们会同时运行:
go fibonacci(c, quit, wg) go pp(c,quit, wg)
尽管在您的情况下这不是必需的并且增加了复杂性。
TA贡献1784条经验 获得超9个赞
通道的make(chan int)
隐式大小为零(
大小为零的通道是无缓冲的。make(chan int, n)
缓冲指定大小的通道。
在这里,c := make(chan int)
是无缓冲的。
如果改变这两行的顺序
go pp(c,quit) fibonacci(c, quit)
到
fibonacci(c,quit)go pp(c,quit)
它会导致程序死锁。在fibonacci
函数中,看select
语句。
select { case c <- x: x, y = y, x+y case q:= <-quit: fmt.Println(q) return }
select
statement 将保持阻塞状态,直到其中一个case
被 fullfilled。由于go pp(c,quit)
在 之后执行fibonacci(c,quit)
,因此没有清除通道c
或向通道发送信号的过程quit
。这就是函数fibonacci(c,quit)
将保持阻塞的原因。
TA贡献1820条经验 获得超9个赞
如果您先调用 fibonnaci,它会在通道上发送值,但接收器尚未准备好。这就是僵局背后的原因。
笔记:
默认情况下,发送和接收阻塞,直到另一方准备就绪。这允许 goroutines 在没有显式锁或条件变量的情况下进行同步。
如果您想更改程序的顺序,看看我们如何避免死锁。
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case q:= <-quit:
return
}
}
}
func pp(c chan int, quit chan int){
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}
func main() {
c := make(chan int)
quit := make(chan int)
go func(){
fibonacci(c, quit)
}()
pp(c,quit)
}
Go 操场上的工作代码
在这种情况下,永远记得等待 go routine 完成。但是当你首先调用 fibonnaci 时它已经发送了值但是接收者还没有准备好导致死锁。
编辑:
因为即使你等待 go routine 完成。它仍然会造成死锁,因为通道不同步为:
包主
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case q:= <-quit:
fmt.Println(q)
return
}
}
}
func pp(c chan int, quit chan int){
defer wg.Done()
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}
func main() {
c := make(chan int)
quit := make(chan int)
fibonacci(c, quit)
wg.Add(1)
go pp(c,quit)
wg.Wait()
}
输出:
致命错误:所有 goroutines 都睡着了——死锁!
goroutine 1 [选择]: main.fibonacci(0x434080, 0x4340c0) /tmp/sandbox779301309/main.go:13 +0xc0 main.main() /tmp/sandbox779301309/main.go:34 +0x80
如果您更改代码并在 for 循环的选择中创建默认情况。然后它将满足这种情况并返回,而您的 main 将退出。永无止境的循环让它在退出的情况下等待返回以使其返回。这将起作用:
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case q, ok := <-quit:
if ok {
fmt.Println(q)
}
return
default:
fmt.Println("No value in any of the channel")
return
}
}
}
func pp(c chan int, quit chan int) {
for i := 0; i < 10; i++ {
if value, ok := <-c; ok {
fmt.Println(value)
}
}
quit <- 0
}
func main() {
c := make(chan int)
quit := make(chan int)
fibonacci(c, quit)
go pp(c, quit)
}
- 3 回答
- 0 关注
- 132 浏览
添加回答
举报