Apakah amalan terbaik di Golang untuk menjejak status penyiapan dua Goroutine dalam Goroutine ketiga? Di Golang, untuk menjejak status penyiapan dua Goroutine dan memproses keputusan mereka dalam Goroutine ketiga, amalan terbaik ialah menggunakan WaitGroup daripada pakej penyegerakan. WaitGroup membolehkan kami menunggu di Goroutine utama untuk menyiapkan Goroutine lain. Pertama, kita perlu mencipta objek WaitGroup dan memanggil kaedah Tambah dalam Goroutine utama untuk menetapkan bilangan Goroutine yang menunggu. Kemudian, kaedah Selesai dipanggil pada penghujung setiap Goroutine untuk menandakan siapnya Goroutine tersebut. Akhir sekali, kaedah Tunggu dipanggil dalam Goroutine ketiga untuk menunggu semua Goroutine selesai. Dengan cara ini, kami boleh menjejak dan memproses hasil kedua-dua Goroutine dengan selamat. Ini ialah amalan terbaik di Golang untuk menjejak status penyiapan berbilang Goroutine.
Saya mempunyai tiga gorout berjalan serentak. Dua daripada mereka melakukan beberapa pemprosesan dan menghantar hasil mereka ke saluran hasil. Goroutine ketiga "mengira" keputusan dengan membaca saluran hasil. Saya boleh menggunakan kumpulan tunggu untuk menunggu dua gorout pengiraan selesai dan kemudian melelang ke atas saluran hasil untuk mengira keputusan, tetapi ini tidak berskala dan memerlukan saya membuat saluran hasil penimbal dengan saiz penimbal yang besar, yang tidak boleh diterima dalam kod pengeluaran.
Saya ingin mengira keputusan semasa pemprosesan sedang berlaku, tetapi saya tidak mahu keluar dari program sebelum semua pengiraan selesai. Apakah amalan terbaik untuk mencapai perkara ini dalam Go?
Ini adalah kaedah semasa saya dan ia berfungsi dengan baik. Saya tertanya-tanya sama ada terdapat cara yang lebih baik kerana ini kelihatan agak kikuk?
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{}{} }
tidak menepati sebarang definisi munasabahbaik. Anda mempunyai gelung for
yang popular di sini:
for { select { case transaction := <-transactions: widgetInventory += transaction case <-salesDone: salesAreDone = true case <-purchasesDone: purchasesAreDone = true default: if salesAreDone && purchasesAreDone { wg.Done() return } } }
Selagi tiada saluran untuk dibaca, kes default
akan dilaksanakan. Ini banyak berlaku kerana cara saluran berfungsi.
Versi kod yang dilaraskan sedikit ini menggambarkan "panas" gelung ini. Hasil yang tepat akan berbeza-beza dan mungkin agak tinggi.
Default case ran 27305 times
Anda tidak mahu kes select
ing 来自通道时,您不希望出现 default
apabila memilih
dari saluran, melainkan lalai itu turut menyekat sesuatu di dalamnya. Jika tidak, anda akan mendapat kitaran haba seperti ini.
nil
saluran yang boleh untuk pemilihan Biasanya dalam pilihan, anda ingin mengenal pasti saluran tertutup dan menetapkan pembolehubah saluran kepada nil
; select
永远不会成功地从 nil
, jadi ini "melumpuhkan" pilihan dengan berkesan. Pertimbangkan versi ubah suai ini
kod: 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)
salesDone
dan purchasesDone
telah "diisyaratkan", kami menutup(transaksi)
. Sebaik sahaja kami kehabisan dan main
共享范围。否则,将 transactions
设置为 nil
将写入一个在 goroutine 之间共享的变量。然而在这种情况下,无论如何,这并不重要,因为我们“知道”我们是最后一个从 transactions
ia ditutup, kami menetapkan
transaksi
kepada akan menulis kepada pembolehubah yang dikongsi antara goroutine. Walau bagaimanapun, dalam kes ini, ia tidak penting, kerana kami "tahu" kami adalah orang terakhir yang membaca daripada transaksi
. transactions
的生产。然后你想排空 transactions
。一旦通道关闭并排空,main
select
来执行此操作。而 select
Jika anda berfikir tentang apa yang anda lakukan di sini, anda perlu menunggu sehingga kedua-dua pengeluar selesai menggandingkan
Anda tidak perlu 🎜 mempunyai kes untuk setiap "pekerja", yang boleh dikatakan agak tidak elegan; anda perlu mengeraskan beberapa pekerja dan mengendalikan saluran "penyelesaian" secara individu. 🎜 🎜Apa yang anda perlu lakukan ialah:🎜
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
的频率远远低于使用等待组的频率。
Atas ialah kandungan terperinci Apakah amalan terbaik di Golang untuk menjejak status penyiapan dua Goroutine dalam Goroutine ketiga?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!