【发布时间】:2019-03-31 10:55:49
【问题描述】:
我编写了一个程序,它可以识别文本文档中的所有唯一单词并计算每个单词出现的次数。为了提高我的程序的性能,我试图将单词计数分解为几个可以并行运行的 goroutine。
最初,我尝试使用通过引用传递给每个 goroutine 的单个映射,其中每个 goroutine 将计算文档部分中的单词。这引起了恐慌,因为程序试图同时从多个 goroutine 写入同一个映射。为了解决这个问题,我创建了一个互斥锁,可以防止多个 goroutine 同时写入映射。此时,程序按预期运行,但与 WordCount 函数的原始顺序实现相比没有性能差异。再想一想,这并不奇怪,因为互斥锁会强制其他 goroutine 在写入映射之前等待,从而阻止并行计算。
下面是使用互斥体来避免所描述的运行时恐慌的代码,但也无法并行计算字数。
func WordCount(words []string, startWord int, endWord int, freqs map[string]int, waitGroup *sync.WaitGroup, mutex *sync.Mutex) {
mutex.Lock()
for i := startWord; i < endWord; i++ {
word := words[i]
freqs[word]++
}
mutex.Unlock()
waitGroup.Done()
}
func ParallelWordCount(text string) map[string]int {
// Split text into string array of the words in text.
text = strings.ToLower(text)
text = strings.ReplaceAll(text, ",", "")
text = strings.ReplaceAll(text, ".", "")
words := strings.Fields(text)
length := len(words)
freqs := make(map[string]int)
var mutex sync.Mutex
var waitGroup sync.WaitGroup
waitGroup.Add(2)
defer waitGroup.Wait()
threads := 2
wordsPerThread := length / threads // always rounds down
wordsInLastThread := length - (threads-1)*wordsPerThread
startWord := -wordsPerThread
var endWord int
for i := 1; i <= threads; i++ {
if i < threads {
startWord += wordsPerThread * i
endWord += wordsPerThread * i
} else {
startWord += wordsInLastThread
endWord += wordsInLastThread
}
go WordCount(words, startWord, endWord, freqs, &waitGroup, &mutex)
}
return freqs
}
我相信,如果我为每个 goroutine 创建一个词频的本地图,并最终将本地频率图与整个文本文件的词数组合成一个图,我就可以实现并行字数统计。我目前面临的问题是如何组合本地频率图。具体来说,我需要知道如何将多个映射组合成一个映射,其给定键的值是要组合的映射中键的值的总和。
为了阐明我正在尝试做的事情的基本逻辑,我包含了以下示例。 ConcurrentSum 函数通过同时计算数组的下半部分和上半部分来返回数组中元素的总和。就我而言,我希望同时计算文本文件不同部分中的字数,并最终将字数组合成一个代表整个文档的字数图。
func sum(a []int, res chan<- int) {
var sum int
for i := 0; i < len(a); i++ {
sum += a[i]
}
res <- sum
}
// concurrently sum the array a.
func ConcurrentSum(a []int) int {
n := len(a)
ch := make(chan int)
go sum(a[:n/2], ch)
go sum(a[n/2:], ch)
return <-ch + <-ch
}
【问题讨论】:
标签: go