Golang中如何解决所有goroutine陷入休眠的死锁错误

Golang中如何解决所有goroutine陷入休眠的死锁错误 我正在尝试编写一个处理大量数据的代码,并希望使用并发。

它需要按相同顺序打印行,但单词应保持正确顺序(这部分工作正常)。

然而,我不断收到“致命错误:所有goroutine都已休眠 - 死锁!”的错误,并且我的行没有按相同顺序排列。

以下是我的代码:https://play.golang.org/p/mw3B4Tqqs7S

5 回复

你好,

你必须在主线程中等待 wg,不需要为它创建一个 goroutine。你应该在一个 goroutine 中对结果进行范围遍历,以避免阻塞主线程。

更多关于Golang中如何解决所有goroutine陷入休眠的死锁错误的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好 Skillian,

你的代码确实解决了死锁问题,但排序仍然存在问题。我做了一些修改,有助于保持行的顺序。

  • 在扫描每一行并进行排序之后,我将该行写入结果,并为每个 goroutine 添加了一个等待,而不是一次性等待所有 goroutine。

参考代码:https://play.golang.org/p/6Tqfd8kawX2

请参考位于 Go Playground - The Go Programming Language 的代码

请参考“请参考位于 Go Playground - The Go Programming Language 的代码”中请求的修复,并告知这是否是您所期望的

你的主 goroutine 正在循环读取 results 通道,而负责关闭该通道的 goroutine 直到循环结束后才会启动。循环会对所有结果调用 writeToFile,但随后会等待通道中永远不会再出现的新结果。

我建议添加一个单独的 goroutine 来负责将结果写入文件,并为其使用第二个等待组。流程基本如下:

  • 创建消费者等待组和 goroutine
  • 创建工作器等待组和 goroutines。当它们完成时,确保调用 Done。
  • 在工作器等待组上调用 Wait。它会在最后一个工作器完成后返回。然后关闭结果通道,并等待消费者的等待组完成。

示例:

func main() {
    fmt.Println("hello world")
}

在Golang中,当所有goroutine都处于等待状态且无法继续执行时,就会发生死锁错误。根据你提供的代码,问题主要在于通道的使用和goroutine同步机制。

以下是修正后的代码示例:

package main

import (
	"fmt"
	"strings"
	"sync"
)

func main() {
	text := "Hello world\nThis is a test\nConcurrency in Go"
	lines := strings.Split(text, "\n")
	
	// 使用带缓冲的通道
	ch := make(chan string, len(lines))
	var wg sync.WaitGroup
	
	// 启动goroutine处理每一行
	for i, line := range lines {
		wg.Add(1)
		go func(idx int, l string) {
			defer wg.Done()
			words := strings.Fields(l)
			for j, w := range words {
				words[j] = fmt.Sprintf("Word %d: %s", j+1, w)
			}
			// 将处理结果发送到通道
			ch <- fmt.Sprintf("Line %d: %s", idx+1, strings.Join(words, " "))
		}(i, line)
	}
	
	// 等待所有goroutine完成
	go func() {
		wg.Wait()
		close(ch)
	}()
	
	// 收集结果
	var results []string
	for result := range ch {
		results = append(results, result)
	}
	
	// 按顺序输出
	for _, result := range results {
		fmt.Println(result)
	}
}

关键修改点:

  1. 使用带缓冲的通道make(chan string, len(lines)) 创建了一个有足够缓冲区的通道,避免发送阻塞

  2. 正确的WaitGroup使用:每个goroutine开始时调用wg.Add(1),结束时调用wg.Done()

  3. 独立的关闭通道goroutine:在单独的goroutine中等待所有工作完成后再关闭通道

  4. 保持顺序的数据结构:使用slice收集结果,而不是依赖通道的顺序

如果你需要严格保持行顺序,可以使用以下版本:

package main

import (
	"fmt"
	"strings"
	"sync"
)

func main() {
	text := "Hello world\nThis is a test\nConcurrency in Go"
	lines := strings.Split(text, "\n")
	
	type result struct {
		idx    int
		output string
	}
	
	ch := make(chan result, len(lines))
	var wg sync.WaitGroup
	
	for i, line := range lines {
		wg.Add(1)
		go func(idx int, l string) {
			defer wg.Done()
			words := strings.Fields(l)
			for j, w := range words {
				words[j] = fmt.Sprintf("Word %d: %s", j+1, w)
			}
			ch <- result{
				idx:    idx,
				output: fmt.Sprintf("Line %d: %s", idx+1, strings.Join(words, " ")),
			}
		}(i, line)
	}
	
	go func() {
		wg.Wait()
		close(ch)
	}()
	
	// 按原始顺序收集结果
	results := make([]string, len(lines))
	for r := range ch {
		results[r.idx] = r.output
	}
	
	for _, r := range results {
		fmt.Println(r)
	}
}

这个版本通过包含索引信息,确保输出顺序与输入顺序完全一致。

回到顶部