Объединение нескольких карт, которые хранятся на канале (значения одного и того же ключа суммируются) в Go - PullRequest
1 голос
/ 02 апреля 2020

Моя цель - создать программу, которая будет подсчитывать вхождения каждого уникального слова в текстовом файле в параллельной форме, все вхождения должны быть представлены в одной карте.

Что я делаю здесь, это деление текстового файла в строку, а затем в массив. Затем этот массив делится на два среза одинаковой длины и одновременно подается в функцию отображения.

   func WordCount(text string)  (map[string]int) {
    wg := new(sync.WaitGroup)
    s := strings.Fields(newText)

    freq := make(map[string]int,len(s))
    channel := make(chan map[string]int,2)

    wg.Add(1)
    go mappers(s[0:(len(s)/2)], freq, channel,wg)
    wg.Add(1)
    go mappers(s[(len(s)/2):], freq, channel,wg)
    wg.Wait()

    actualMap := <-channel


    return actualMap
func mappers(slice []string, occurrences map[string]int, ch chan map[string]int, wg *sync.WaitGroup)  {
    var l = sync.Mutex{}
    for _, word := range slice {
        l.Lock()
        occurrences[word]++
        l.Unlock()

    }
    ch <- occurrences
    wg.Done()
}

Суть в том, что я получаю огромную многострочную ошибку, которая начинается с

фатальная ошибка: запись одновременной карты

Когда я запускаю код. Который, как я думал, охранялся через взаимное исключение

        l.Lock()
        occurrences[word]++
        l.Unlock()

Что я здесь не так делаю? И, кроме того. Как я могу объединить все карты в канале? И под объединением я подразумеваю, что те же значения ключа суммируются на новой карте.

1 Ответ

1 голос
/ 02 апреля 2020

Основная проблема в том, что вы используете отдельный замок в каждой программе. Это не поможет сериализовать доступ к карте. Одна и та же блокировка должна использоваться в каждой процедуре.

И поскольку вы используете одну и ту же карту в каждой программе, вам не нужно объединять их, и вам не нужен канал для доставки результата.

Даже если вы используете один и тот же мьютекс в каждой программе, так как вы используете одну карту, это, вероятно, не повлияет на производительность, программы должны будут конкурировать друг с другом за блокировку карты.

Вы должны создать отдельную карту в каждой процедуре, использовать ее для локального подсчета, а затем доставить карту результатов на канал. Это может дать вам повышение производительности.

Но тогда вам не нужна блокировка, поскольку у каждой программы будет своя собственная карта, которую она может читать / записывать без мьютекса.

Но тогда вам нужно будет доставить результат по каналу, а затем объединить его.

И поскольку программы доставляют результаты по каналу, группа ожидания становится ненужной.

func WordCount(text string) map[string]int {
    s := strings.Fields(text)

    channel := make(chan map[string]int, 2)

    go mappers(s[0:(len(s)/2)], channel)
    go mappers(s[(len(s)/2):], channel)

    total := map[string]int{}
    for i := 0; i < 2; i++ {
        m := <-channel
        for k, v := range m {
            total[k] += v
        }
    }

    return total
}

func mappers(slice []string, ch chan map[string]int) {
    occurrences := map[string]int{}
    for _, word := range slice {
        occurrences[word]++

    }
    ch <- occurrences
}

Пример тестирования этого :

fmt.Println(WordCount("aa ab cd cd de ef a x cd aa"))

Вывод (попробуйте на Go Playground ):

map[a:1 aa:2 ab:1 cd:3 de:1 ef:1 x:1]

Также обратите внимание, что в теории это выглядит "хорошо", но в На практике вы все еще не можете добиться какого-либо повышения производительности, поскольку программы выполняют слишком «небольшую» работу, а их запуск и объединение результатов требуют усилий, которые могут перевесить преимущества.

...