Heim > Artikel > Backend-Entwicklung > Was ist die beste Vorgehensweise in Golang, um den Abschlussstatus von zwei Goroutinen innerhalb einer dritten Goroutine zu verfolgen?
Was ist die beste Vorgehensweise in Golang, um den Abschlussstatus von zwei Goroutinen in einer dritten Goroutine zu verfolgen? Um in Golang den Abschlussstatus von zwei Goroutinen zu verfolgen und ihre Ergebnisse in einer dritten Goroutine zu verarbeiten, besteht die beste Vorgehensweise darin, WaitGroup aus dem Synchronisierungspaket zu verwenden. WaitGroup ermöglicht es uns, in der Haupt-Goroutine auf die Fertigstellung anderer Goroutinen zu warten. Zuerst müssen wir ein WaitGroup-Objekt erstellen und die Add-Methode in der Haupt-Goroutine aufrufen, um die Anzahl der wartenden Goroutinen festzulegen. Anschließend wird am Ende jeder Goroutine die Done-Methode aufgerufen, um den Abschluss dieser Goroutine zu signalisieren. Schließlich wird in der dritten Goroutine die Wait-Methode aufgerufen, um auf den Abschluss aller Goroutinen zu warten. Auf diese Weise können wir die Ergebnisse beider Goroutinen sicher verfolgen und verarbeiten. Dies ist die beste Vorgehensweise in Golang, um den Abschlussstatus mehrerer Goroutinen zu verfolgen.
Ich habe drei Goroutinen gleichzeitig laufen lassen. Zwei von ihnen führen eine Verarbeitung durch und senden ihre Ergebnisse an den Ergebniskanal. Die dritte Goroutine „zählt“ die Ergebnisse, indem sie die Ergebniskanäle liest. Ich könnte eine Wartegruppe verwenden, um auf den Abschluss der beiden Berechnungs-Goroutinen zu warten und dann über die Ergebniskanäle zu iterieren, um die Ergebnisse zu zählen, aber das lässt sich nicht skalieren und erfordert, dass ich einen gepufferten Ergebniskanal mit einer riesigen Puffergröße erstelle, was inakzeptabel ist im Produktionscode.
Ich möchte die Ergebnisse zählen, während die Verarbeitung läuft, aber ich möchte das Programm nicht beenden, bevor die Zählung abgeschlossen ist. Was sind die Best Practices, um dies in Go zu erreichen?
Das ist meine aktuelle Methode und sie funktioniert großartig. Ich frage mich, ob es einen besseren Weg gibt, da das etwas umständlich erscheint?
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{}{} }
passt keiner vernünftigen Definitiongut. Sie haben hier eine beliebte for
-Schleife:
for { select { case transaction := <-transactions: widgetInventory += transaction case <-salesDone: salesAreDone = true case <-purchasesDone: purchasesAreDone = true default: if salesAreDone && purchasesAreDone { wg.Done() return } } }
Der default
-Fall wird ausgeführt, solange kein Kanal zum Lesen vorhanden ist. Dies geschieht aufgrund der Art und Weise, wie Kanäle funktionieren, häufig.
Diese leicht angepasste Version des Codes veranschaulicht die „Hitze“ dieser Schleife. Die genauen Ergebnisse variieren und können recht hoch ausfallen.
Default case ran 27305 times
Sie möchten den select
ing 来自通道时,您不希望出现 default
-Fall nicht haben, wenn Sie aus einem Kanal auswählen
, es sei denn, diese Standardeinstellung blockiert auch etwas darin. Sonst kommt es zu solchen Temperaturwechseln.
nil
fähige Kanäle zur Auswahl Normalerweise möchten Sie in einer Auswahl einen geschlossenen Kanal identifizieren und die Kanalvariable auf nil
; select
永远不会成功地从 nil
select
wird nie erfolgreich aus dem -Kanal lesen, sodass die Auswahl effektiv „deaktiviert“ wird. Betrachten Sie diese modifizierte Version
desCodes: salesDone
和 purchasesDone
都被“发出信号”,我们 close(transactions)
。一旦我们耗尽 transactions
并且它被关闭,我们将 transactions
设置为 nil。我们在 transactions
不为 nil 时循环,在这段代码中,意味着所有通道都是 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)Mit diesen Anpassungen am Verbraucher haben wir keine Hot Loops mehr; wir blockieren immer, bis Daten aus dem Kanal gelesen werden. Sobald sowohl
salesDone
als auch purchasesDone
„signalisiert“ wurden, schließen wir (Transaktionen)
. Sobald wir transactions
erschöpft haben und main
共享范围。否则,将 transactions
设置为 nil
将写入一个在 goroutine 之间共享的变量。然而在这种情况下,无论如何,这并不重要,因为我们“知道”我们是最后一个从 transactions
es geschlossen ist, setzen wir transactions
auf Null. Wir durchlaufen eine Schleife, wenn transactions
nicht Null ist, was in diesem Code bedeutet, dass alle Kanäle
main
teilt. Andernfalls wird durch Setzen von transactions
auf in eine Variable geschrieben, die von Goroutinen gemeinsam genutzt wird. In diesem Fall spielt es jedoch sowieso keine Rolle, da wir „wissen“, dass wir die letzten sind, die aus transactions
lesen. transactions
的生产。然后你想排空 transactions
。一旦通道关闭并排空,main
select
来执行此操作。而 select
Wenn Sie darüber nachdenken, was Sie hier tun, müssen Sie warten, bis beide Produzenten die Paarung abgeschlossen haben
Sie müssen nicht 🎜 für jeden „Arbeiter“ einen Fall haben, was wohl ziemlich unelegant ist; Sie müssen mehrere Arbeiter fest codieren und den „Abschluss“-Kanal einzeln behandeln. 🎜 🎜Was Sie tun müssen, ist:🎜
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
的频率远远低于使用等待组的频率。
Das obige ist der detaillierte Inhalt vonWas ist die beste Vorgehensweise in Golang, um den Abschlussstatus von zwei Goroutinen innerhalb einer dritten Goroutine zu verfolgen?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!