亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

意外的協程行為

意外的協程行為

Go
白板的微信 2023-06-01 17:02:39
我是 Golang 的初學者我從這里閱讀了 Go 中的并發性。在第 8 張幻燈片上出現問題之前,一切都很順利。問題是:找出兩個給定的二叉樹是否等價。我的方法:進行中序遍歷,將兩棵樹的值保存在一個切片中并進行比較。這是我的解決方案:[不完整]package mainimport (    "fmt"    "golang.org/x/tour/tree")// Walk walks the tree t sending all values// from the tree to the channel ch.func Walk(t *tree.Tree, ch chan int) {    if t != nil {        Walk(t.Left, ch)        ch <- t.Value        Walk(t.Right, ch)    }}// Same determines whether the trees// t1 and t2 contain the same values.func Same(t1, t2 *tree.Tree) bool {    ch1 := make(chan int)    ch2 := make(chan int)    go func() {        fmt.Println("executing first go routing")        Walk(t1, ch1)        fmt.Println("closing channel [ch1]")        close(ch1)    }()    go func() {        fmt.Println("executing second go routing")        Walk( t2, ch2 )        fmt.Println("closing channel [ch2]")        close(ch2)    }()    shouldContinue := true    var continue1, continue2 bool    for shouldContinue {        select {        case r1, ok1 := <-ch1:            fmt.Println("[ch1] [rcvd]", r1)            continue1 = ok1        case r2, ok2 := <-ch2:            fmt.Println("[ch2] [rcvd]", r2)            continue2 = ok2        }        shouldContinue = continue1 || continue2    }    return true}func main() {    Same(tree.New(1), tree.New(1))}我知道 goroutines 是合作調度的,如果它正在循環或連續計算,一個和完全阻塞另一個。所以我預計對于輸出,它會首先從任一通道接收值,關閉它,然后它會從另一個通道接收值,然后關閉。一旦兩者都關閉,for 循環就會中斷。令我驚訝的是,第一個 go 例程從未被安排好。這是我收到的輸出:executing second go routing[ch2] [rcvd] 1[ch2] [rcvd] 2[ch2] [rcvd] 3[ch2] [rcvd] 4[ch2] [rcvd] 5[ch2] [rcvd] 6[ch2] [rcvd] 7[ch2] [rcvd] 8[ch2] [rcvd] 9[ch2] [rcvd] 10closing channel [ch2][ch2] [rcvd] 0 誰能解釋這里發生了什么?一旦 channel2 關閉并且第二個例程完成,為什么第一個不執行?任何幫助,將不勝感激。謝謝。
查看完整描述

2 回答

?
蠱毒傳說

TA貢獻1895條經驗 獲得超3個贊

關閉 channel2 后,為什么第一個不執行?

通道不執行。一遍又一遍地執行的是您的選擇。請注意,無論通道是否關閉,這兩種情況都可以始終執行。所以 select 可以選擇 id 所做的第二種情況,而你中止了。(你的中止條件看起來可疑:一旦兩個通道都關閉,即如果ok1和 ok2 都為假,你就完成了)。

不要將 select 本身視為“goroutine 調度工具”。它不是。它將隨機選擇一個可運行的案例。如果您所有的案例都是可val, ok := <- ch運行的,那么 select 可能總是選擇第二個?;虻谝粋€,或...

[第二個解決方案]我現在更加困惑是怎么回事了。

您的中止條件不同。一旦兩個通道都為零,您就會中斷,這會在兩個通道都關閉后發生。這與您的第一個解決方案不同,因為一旦任何 一個通道關閉,第一個就會中斷。

這里的并發性問題不是 goroutine 調度,而只是你的 for 循環執行選擇的中止條件。它們不同于第一個和第二個,第一個從根本上是錯誤的,因為一旦任何通道耗盡它就會停止。


查看完整回答
反對 回復 2023-06-01
?
嚕嚕噠

TA貢獻1784條經驗 獲得超7個贊

在你的代碼的第一部分,你的邏輯有錯誤。


shouldContinue := true

var continue1, continue2 bool

for shouldContinue {

? ? select {

? ? case r1, ok1 := <-ch1:

? ? ? ? fmt.Println("[ch1] [rcvd]", r1)

? ? ? ? continue1 = ok1


? ? case r2, ok2 := <-ch2:

? ? ? ? fmt.Println("[ch2] [rcvd]", r2)

? ? ? ? continue2 = ok2

? ? }

? ? shouldContinue = continue1 || continue2

}

在上面的代碼中,continue1和continue2是false. select在他的一個案例完成之前一直處于阻塞狀態。讓我們先說case r2, ok2 := <-ch2:滿足,然后continue2才是true。由于shouldContinue = continue1 || continue2這種情況,for循環將繼續。出于某種原因(去例行調度)case r2, ok2 := <-ch2:條件每次都滿足?,F在當關閉時ch2,價值ok2也會false如此?,F在, 和都是,也將是。因此它打破了循環,你看不到輸出。試試這個:continue2falsecontinue1continue2falseshouldContinuefalseforch1


continue1 = true

continue2 = true

for shouldContinue {

? ? select {

? ? case r1, ok1 := <-ch1:

? ? ? ? fmt.Println("[ch1] [rcvd]", r1)

? ? ? ? continue1 = ok1


? ? case r2, ok2 := <-ch2:

? ? ? ? fmt.Println("[ch2] [rcvd]", r2)

? ? ? ? continue2 = ok2

? ? }

? ? shouldContinue = continue1 || continue2

}

當一個通道關閉時,你不能在這個通道上發送值,但你仍然可以從通道接收。


Nil 通道總是阻塞,并且您還更改了for循環中斷邏輯。這就是您的第二個解決方案有效的原因。


查看完整回答
反對 回復 2023-06-01
  • 2 回答
  • 0 關注
  • 156 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號