[英]How can I read from a channel after my GoRoutines have finished running?
我目前有兩個函數 pushNodes(node) 和 updateNodes(node)。 在 pushNodes function 中,我通過要在 updateNodes 中使用的通道推送值。 為了保存准確的通道值,我需要在啟動 updateNodes() 之前完成所有 pushNodes go 例程。 GoRoutines 完成執行后,如何仍然訪問通道值?
我不斷收到“致命錯誤:所有 goroutines 都睡着了 - 死鎖”。 請讓我知道如何從頻道中獲取這些值? 有沒有更好/替代的方法來做到這一點?
//pushNodes is a function that will push the nodes values
func pushNodes(node Node) {
defer wg.Done()
fmt.Printf("Pushing: %d \n", node.number)
//Choose a random peer node
var randomnode int = rand.Intn(totalnodes)
for randomnode == node.number {
rand.Seed(time.Now().UnixNano())
randomnode = rand.Intn(totalnodes)
}
//If the current node is infected, send values through the channel
if node.infected {
sentchanneldata := ChannelData{infected: true, message: node.message}
allnodes[randomnode].channel <- sentchanneldata
fmt.Printf("Node %d sent a value of %t and %s to node %d!\n", node.number, sentchanneldata.infected, sentchanneldata.message, allnodes[randomnode].number)
}
//updateNodes is a function that will update the nodes values
func updateNodes(node Node) {
defer wg.Done()
fmt.Printf("Updating: %d\n", node.number)
//get value through node channel
receivedchanneldata := <-node.channel
fmt.Printf("Node %d received a value of %t and %s!\n", node.number, receivedchanneldata.infected, receivedchanneldata.message)
// update value
if receivedchanneldata.infected == true {
node.infected = true
}
if receivedchanneldata.message != "" {
node.message = receivedchanneldata.message
}
fmt.Printf("Update successful!\n")
}
//Part of main functions
wg.Add(totalnodes)
for node := range allnodes {
go pushNodes(allnodes[node])
}
wg.Wait()
fmt.Println("Infect function done!")
wg.Add(totalnodes)
for node := range allnodes {
go updateNodes(allnodes[node])
}
wg.Wait()
GoRoutines 完成執行后,如何仍然訪問通道值?
通道的存在,包括任何已被推入其中的數據,都獨立於可能讀取或寫入它的 goroutines,前提是至少有一個 goroutine 仍然存在可以讀取和/或寫入它。 (一旦所有此類 goroutine 都消失了,通道最終將被 GC 處理。)
您的代碼示例無法使用( 如前所述),因此我們無法准確說明您哪里出錯了,但您會收到您在此處報告的那種fatal
消息:
致命錯誤:所有 goroutines 都睡着了——死鎖!
如果您嘗試從最后一個可運行的 goroutine 中的通道讀取,這樣這個 goroutine 就會進入睡眠狀態以等待該通道上的消息,這樣 Go 運行時的 rest 可以確定沒有當前處於睡眠狀態的 goroutine將永遠醒來並在該頻道上傳遞消息。 例如,假設您總共有 7 個 goroutine 正在運行,其中一個運行到以下代碼行:
msg = <-ch
其中ch
是一個開放通道,目前沒有可用數據。 這 7 個 goroutines 中的一個到達這條線並阻塞(“進入睡眠”),等待剩下的六個goroutines 之一做:
ch <- whatever
這將喚醒第 7 個 goroutine。 所以現在只有 6 個 goroutines 可以寫入ch
或關閉ch
。 如果這六個剩余的 goroutines也通過同一條線,一次一個或幾個或一次全部,並且它們都沒有在通道上發送或關閉它,那么那些剩余的 goroutines 也會阻塞。 當它們中的最后一個阻塞時,運行時將意識到程序被卡住了,並且 panic。
然而,如果剩下的六個 goroutines 中只有五個像這樣阻塞,那么第六個 goroutines 運行通過一行讀取:
close(ch)
該close
操作將關閉通道,導致所有六個“陷入沉睡”的 goroutine 接收到由零值“假”消息msg
表示的“數據結束”。 您還可以使用接收的二值形式:
msg, ok = <-ch
如果通道未關閉且msg
包含真實消息,則ok
為true
,但如果通道關閉且msg
現在包含零值“ false
”消息,則為假。
因此,您可以:
前者是通道的常態,沒有辦法提前知道應該在通道上發送多少消息。 即使您知道,它仍然可以使用。 進行關閉的典型構造是:
ch := make(chan T) // for some type T
// do any other setup that is appropriate
var wg sync.WaitGroup
wg.add(N) // for some number N
// spin off some number of goroutines N, each of which may send
// any number of messages on the channel
for i := 0; i < N; i++ {
go doSomething(&wg, ch)
// in doSomething, call wg.Done() when done sending on ch
}
go func() {
wg.Wait() // wait for all N goroutines to finish
close(ch) // then, close the channel
}()
// Start function(s) that receive from the channel, either
// inline or in more goroutines here; have them finish when
// they see that the channel is closed.
這種模式依賴於創建一個額外的第 N+1 個 goroutine 的能力——這是匿名的 function go func() {... }()
序列——它一生的全部工作就是等待所有發送者說我是發送完畢。 每個發件人通過調用wg.Done()
一次來完成此操作。 這樣,沒有發送者對關閉通道有任何特殊責任:他們都只是寫,然后在寫完后宣布“我寫完了”。 一個goroutine 有一個特殊的職責:它等待所有發送者宣布“我寫完了”,然后它關閉通道並退出,完成了它一生中的一項工作。
所有的接收者——無論是一個還是多個——現在很容易知道什么時候沒有人會再發送任何東西,因為他們在那個時候看到了一個關閉的通道。 因此,如果大部分工作都在發送端,您甚至可以在此處使用帶有簡單for... range ch
循環的主 goroutine。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.