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 關注
- 187 瀏覽
添加回答
舉報