将多个映射组合到一个映射中,该映射的给定键值是组合映射中的键值之和

时间:2019-03-31 10:55:50

标签: go

我编写了一个程序,该程序可以识别文本文档中的所有唯一单词并计算每个单词出现的次数。为了提高程序的性能,我试图将字数分解为几个可以并行运行的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
}

1 个答案:

答案 0 :(得分:0)

我相信您可以创建一个映射数组,每个映射用于每个进程,然后使用列表读取每个映射,以跟踪您已经计数的单词。假设每个单词都是计数次数的关键,那就是它的外观。      考虑到并发方面,此处并行处理可能不是最佳选择,因为要真正提高性能,必须将所有内容分开保存。如果您有存储空间,那么您当然可以使用列表,并在最坏的情况下通过集成地图获得O(N)效率。您将需要将地图的集成保持在单个线程或单个进程中。