Golang文本处理中遇到的死锁问题(新手求助)

Golang文本处理中遇到的死锁问题(新手求助) 这是一个初学者问题,但我无法理解为什么会出现死锁。这段代码本应将字符串数组分成四块,通过 goroutine 发送到频率计数器,然后打印结果。

// Split load optimally across processor cores.
func WordCount(text string) {

	//doing text processing here (omitted)

	numberOfGroups := 4
	piece := len(sentence) / numberOfGroups
        //buffered channel, but it does not work better with an unbuffered one
	wordChannel := make(chan map[string]int, numberOfGroups)

	wg := new(sync.WaitGroup)
	wg.Add(numberOfGroups)

	for i := 0; i+piece < len(sentence); i += piece {
		go processToCounting(sentence[i:i+piece], wordChannel, wg)
	}

	wg.Wait()
	close(wordChannel)
	fmt.Print(<-wordChannel)

}

func processToCounting(textSlice []string, wordChannel chan map[string]int, wg *sync.WaitGroup) {
	freq := make(map[string]int)
	for _, v := range textSlice {
		freq[v]++
	}

        //does not work with defer either
        wg.Done()
	wordChannel <- freq
}

更多关于Golang文本处理中遇到的死锁问题(新手求助)的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

谢谢。你介意解释一下原因吗?

更多关于Golang文本处理中遇到的死锁问题(新手求助)的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


非常感谢,我十分感激。确实,刚开始学习 Go 语言时会感到非常困惑。在其他练习中,我把 wg.Add(1) 放在了循环内部,所以我明白你在说什么。

@Dj_Sparks

你需要将 wg.Wait() 放入一个 goroutine 中,这样它就不会阻塞。例如,通常你会看到这样实现:

go func() {
	wg.Wait()
	close(wordChannel)
}()

fmt.Print(<-wordChannel)

当然。在你的示例中,你向 wg 添加了 numberOfGroups。但是有可能 for i := 0; i+piece < len(sentence); i += piece 这行代码执行的次数少于 numberOfGroups 次(4次),你永远没有返回第4次 wg.Done()

例如,你可以改为这样做,并在每次循环时增加 wg 计数器:

for i := 0; i+piece < len(sentence); i += piece {
	wg.Add(1)
	go processToCounting(sentence[i:i+piece], wordChannel, wg)
}

但你仍然可能需要像这样完成:

go func() {
	wg.Wait()
	close(wordChannel)
}()

for val := range wordChannel {
	fmt.Println(val)
}

编辑:或者在你的情况下,最终计数可以这样做:

go func() {
	wg.Wait()
	close(wordChannel)
}()

frequencyMap := map[string]int{}
for val := range wordChannel {
	for k, v := range val {
		frequencyMap[k] += v
	}
}

fmt.Println(frequencyMap)

希望这能讲得通!

问题在于代码在关闭通道后只从通道读取一次,但实际发送了多个值。当有多个 goroutine 向通道发送数据时,只读取一次会导致其他发送操作被阻塞,从而产生死锁。

以下是修正后的代码:

func WordCount(text string) {
    // 假设 sentence 是字符串切片
    sentence := strings.Fields(text) // 示例:将文本分割成单词
    
    numberOfGroups := 4
    if len(sentence) < numberOfGroups {
        numberOfGroups = len(sentence)
    }
    
    piece := len(sentence) / numberOfGroups
    wordChannel := make(chan map[string]int, numberOfGroups)

    wg := new(sync.WaitGroup)
    wg.Add(numberOfGroups)

    for i := 0; i < len(sentence); i += piece {
        end := i + piece
        if end > len(sentence) {
            end = len(sentence)
        }
        go processToCounting(sentence[i:end], wordChannel, wg)
    }

    // 使用单独的 goroutine 等待并关闭通道
    go func() {
        wg.Wait()
        close(wordChannel)
    }()

    // 读取所有结果
    for result := range wordChannel {
        fmt.Println(result)
    }
}

func processToCounting(textSlice []string, wordChannel chan map[string]int, wg *sync.WaitGroup) {
    defer wg.Done() // 使用 defer 确保 Done() 被调用
    
    freq := make(map[string]int)
    for _, v := range textSlice {
        freq[v]++
    }
    wordChannel <- freq
}

主要修改:

  1. 使用 range 循环读取通道中的所有值
  2. 在单独的 goroutine 中等待并关闭通道
  3. 使用 defer wg.Done() 确保计数器正确递减
  4. 添加边界检查防止切片越界
  5. 处理当文本长度小于分组数的情况

这样确保所有发送到通道的值都被正确接收,避免了死锁。

回到顶部