我正在做一些必須是常見模式的事情,但我看不出應該如何處理。
在這個人為的例子中,我有一個計算字串中字母的函式,我希望它在切片中的每個元素上運行,并將結果存盤在地圖中,所以
[]string = {"one", "two", "three"}
產量
map[string]int = {"one":3, "two":3, "three":5}
我正在使用該guard模式,以確保cores在任何時候僅運行多個 goroutine(我認為將并發 goroutine 的數量設定為系統上的虛擬處理器的數量一定是理想的?)
const cores int = 2
var words = []string{"hello", "there", "this", "is", "a", "list", "of", "words"}
type result struct {
name string
res int
}
func count_letters(word string, cGuard chan struct{}, cResults chan result) {
time.Sleep(1 * time.Second)
fmt.Println(word)
<-cGuard
cResults <- result{word, len(word)}
}
func main() {
cGuard := make(chan struct{}, cores)
cResults := make(chan result, cores)
mResults := map[string]int{}
for _, name := range words {
cGuard <- struct{}{}
// Need to populate mResults with the output from cResults
go count_letters(name, cGuard, cResults)
}
fmt.Scanln()
}
這可行,但我不確定如何將result結構從cResults通道中取出以行內填充地圖。
我可以將緩沖區大小設定cResults為len(words),然后等到 for 回圈完成,然后將它們全部拉出,但這似乎很不雅,如果 的長度words很大,會出現問題嗎?
uj5u.com熱心網友回復:
對于這個特定的用例,作業池模式會更合適。
在您的示例中,您為每個單詞啟動了一個單獨的 goroutine,雖然 go 可以處理這個問題,但它不是很有效,因為運行時必須啟動一個新的 go 例程并停止舊的例程,同時跟蹤所有這些。
有了一個作業池,我們可以根據需要準確地啟動 goroutine 的數量,并通過一個通道給作業人員任務。這減少了很多開銷,作業人員總是相同的 goroutine。結果的收集也是通過一個渠道完成的。并使用 WaitGroup 確保我們不會在所有作業人員完成之前終止。
這是您的示例的作業池版本:
package main
import (
"fmt"
"sync"
"time"
)
// 2 for testing, in the real world runtime.NumCPU() would be used
const cores int = 2
var words = []string{"hello", "there", "this", "is", "a", "list", "of", "words"}
type result struct {
name string
res int
}
func count_letters(wg *sync.WaitGroup, cWords chan string, cResults chan result) {
// Tell the waitgroup we are done once we return
defer wg.Done()
// Read from cWords until it is closed, at which point we return
for word := range cWords {
time.Sleep(1 * time.Second)
cResults <- result{word, len(word)}
}
}
func main() {
cWords := make(chan string)
cResults := make(chan result)
// This waitgroup will later be used to wait for all worker to be done
var wg sync.WaitGroup
for i := 0; i < cores; i {
// Add 1 before starting the goroutine
wg.Add(1)
go count_letters(&wg, cWords, cResults)
}
// Collect the results via a goroutine, since we need to submit tasks and collect results at the same time
mResults := map[string]int{}
go func() {
for result := range cResults {
mResults[result.name] = result.res
}
}()
// Insert all words into the cWords chan
for _, word := range words {
cWords <- word
}
// After all words have been inserted, close the channel, this will cause the workers to exit
// once all words have been read from the channel
close(cWords)
// Wait for all workers to be done
wg.Wait()
// Close the results chan, this will terminate our collection go routine, good practice but not necessary in this
// specific example
close(cResults)
// Use the results
fmt.Println(mResults)
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/419548.html
標籤:
