Golang 中追蹤第三個 Goroutine 中兩個 Goroutine 的完成狀態的最佳實踐是什麼? 在 Golang 中,要追蹤兩個 Goroutine 的完成狀態並在第三個 Goroutine 中處理它們的結果,最佳實踐是使用 sync 套件中的 WaitGroup。 WaitGroup 讓我們在主 Goroutine 中等待其他 Goroutine 的完成。首先,我們需要建立一個 WaitGroup 對象,並在主 Goroutine 中呼叫 Add 方法來設定等待的 Goroutine 數量。然後,在每個 Goroutine 的末尾呼叫 Done 方法來表示該 Goroutine 的完成。最後,在第三個 Goroutine 中呼叫 Wait 方法來等待所有 Goroutine 的完成。這樣,我們就可以安全地追蹤並處理兩個 Goroutine 的結果了。這是 Golang 中追蹤多個 Goroutine 完成狀態的最佳實踐。
我有三個並發運行的 goroutine。其中兩個進行一些處理並將其結果發送到結果通道。第三個 goroutine 透過讀取結果通道來「統計」結果。我可以使用 waitgroup 等待兩個計算 goroutine 完成,然後遍歷結果通道來統計結果,但這無法擴展,並且需要我創建一個具有巨大緩衝區大小的緩衝結果通道,這是不可接受的在生產代碼中。
我想在處理發生時統計結果,但在所有統計完成之前我不想退出程式。在 Go 中實現這一目標的最佳實踐是什麼?
這是我目前的方法,效果很好。我想知道是否有更好的方法,因為這看起來有點笨拙?
package main import ( "fmt" "sync" ) type T struct{} func main() { var widgetInventory int = 1000 transactions := make(chan int, 100) salesDone := make(chan T) purchasesDone := make(chan T) var wg sync.WaitGroup fmt.Println("Starting inventory count = ", widgetInventory) go makeSales(transactions, salesDone) go newPurchases(transactions, purchasesDone) wg.Add(1) go func() { salesAreDone := false purchasesAreDone := false for { select { case transaction := <-transactions: widgetInventory += transaction case <-salesDone: salesAreDone = true case <-purchasesDone: purchasesAreDone = true default: if salesAreDone && purchasesAreDone { wg.Done() return } } } }() wg.Wait() fmt.Println("Ending inventory count = ", widgetInventory) } func makeSales(transactions chan int, salesDone chan T) { for i := 0; i < 3000; i++ { transactions <- -100 } salesDone <- struct{}{} } func newPurchases(transactions chan int, purchasesDone chan T) { for i := 0; i < 3000; i++ { transactions <- 100 } purchasesDone <- struct{}{} }
不適合任何合理的定義很好。您在這裡有一個熱門的 for
循環:
for { select { case transaction := <-transactions: widgetInventory += transaction case <-salesDone: salesAreDone = true case <-purchasesDone: purchasesAreDone = true default: if salesAreDone && purchasesAreDone { wg.Done() return } } }
只要沒有通道可供讀取,就會執行 default
案例。由於渠道的工作方式,這種情況經常發生。
這個稍作調整的程式碼版本說明了此循環的「熱度」。 確切的結果會有所不同,可能會相當高。
Default case ran 27305 times
當 select
ing 來自通道時,您不希望出現 default
情況,除非該預設也會阻止其中的某些內容。否則你會得到這樣的熱循環。
nil
able 頻道進行選擇通常在選擇中,您想要識別關閉的通道並將通道變數設為nil
#; select
永遠不會成功地從nil
通道讀取內容,因此這實際上「禁用」了該選擇。
考慮程式碼的此修改版本:
go func(transactions chan int, salesDone <-chan T, purchasesDone <-chan T) { defer wg.Done() for transactions != nil { select { case transaction, ok := <-transactions: if ok { widgetInventory += transaction } else { transactions = nil } case <-salesDone: salesDone = nil if purchasesDone == nil { close(transactions) } case <-purchasesDone: purchasesDone = nil if salesDone == nil { close(transactions) } } } }(transactions, salesDone, purchasesDone)
透過對消費者的這些調整,我們不再有熱循環;我們總是阻塞直到從通道讀取資料。一旦 salesDone
和 purchasesDone
都被“發出信號”,我們 close(transactions)
。一旦我們耗盡 transactions
並且它被關閉,我們將 transactions
設定為 nil。我們在 transactions
不為 nil 時循環,在這段程式碼中,意味著所有通道都是 nil
。
微妙但重要的一點:我將通道傳遞給此函數,因此它的參考不與 main
共享範圍。否則,將 transactions
設定為 nil
將寫入一個在 goroutine 之間共享的變數。然而在這種情況下,無論如何,這並不重要,因為我們「知道」我們是最後一個從 transactions
讀取的內容。
如果您考慮您在這裡所做的事情,您需要等到兩個生產者都完成對 transactions
的生產。然後你想排空 transactions
。一旦通道關閉並排空,main
就知道求和已完成。
您不需要 select
來執行此操作。而 select
為每個「工人」都有一個案例,可以說是相當不優雅的;您必須對多個工作人員進行硬編碼並單獨處理「完成」通道。
您需要做的是:
var resultswgsync.WaitGroup
之外,还为消费者添加一个。defer wg.Done()
defer resultswg.Done()
在遍历 transactions
之前:
go func() { defer resultswg.Done() for transaction := range transactions { widgetInventory += transaction } }()
wg.Wait() close(transactions) resultswg.Wait()
package main import ( "fmt" "sync" ) func main() { var widgetInventory int = 1000 transactions := make(chan int, 100) var wg, resultswg sync.WaitGroup fmt.Println("Starting inventory count = ", widgetInventory) wg.Add(2) go makeSales(transactions, &wg) go newPurchases(transactions, &wg) resultswg.Add(1) go func() { defer resultswg.Done() for transaction := range transactions { widgetInventory += transaction } }() wg.Wait() close(transactions) resultswg.Wait() fmt.Println("Ending inventory count = ", widgetInventory) } func makeSales(transactions chan int, wg *sync.WaitGroup) { defer wg.Done() for i := 0; i < 3000; i++ { transactions <- -100 } } func newPurchases(transactions chan int, wg *sync.WaitGroup) { defer wg.Done() for i := 0; i < 3000; i++ { transactions <- 100 } }
您可以在这里看到,在此模式中可以有任意数量的生产者;您只需为每个生产者添加 wg.Add(1)
即可。
当我不知道每个工作人员会返回多少结果时,我一直使用这种模式来并行化工作。我发现它很容易理解,并且比尝试 select
多个通道简单得多。事实上,我什至想说,如果您发现自己从多个渠道进行 select
ing,您应该退后一步,确保它对您来说确实有意义。我使用 select
的频率远远低于使用等待组的频率。
以上是Golang 中追蹤第三個 Goroutine 中兩個 Goroutine 的完成狀態的最佳實踐是什麼?的詳細內容。更多資訊請關注PHP中文網其他相關文章!