如何实现Goroutine任务进度监控并解决死锁问题?
问题排查与修复方案
关键问题分析
- 函数参数类型错误:
task函数的registers string[]类型不匹配,调用时传入的是int切片,应改为[]int。status <-chan int是只读通道,但函数需要向通道发送数据,应改为chan<- int(只写通道)。
- 切片范围计算错误:
循环中分割切片时,(partial*i)+1会跳过每个分段的第一个元素,导致总处理元素数量不足16个,正确分段应为registers[partial*i : partial*(i+1)]。 - 通道接收逻辑错误:
原代码的for len(status) > 0 && <-status > 0会在每次循环中两次读取通道值(一次在条件判断,一次在count += <-status),导致统计结果错误;且当通道暂时为空时会直接退出循环,无法等待所有任务完成。 - 未等待所有Goroutine完成:
无同步机制等待所有taskGoroutine执行完毕,主Goroutine可能提前退出,导致部分任务未处理,甚至引发Goroutine阻塞泄漏。
修复后的完整代码
import ( "fmt" "log" "net/http" "sync" ) func task(registers []int, status chan<- int, wg *sync.WaitGroup) { defer wg.Done() for range registers { status <- 1 // 每处理一个元素,发送1表示完成一个任务单元 } } func main() { status := make(chan int, 16) // 缓冲设为16,避免Goroutine因通道满阻塞 http.HandleFunc("/process", func(w http.ResponseWriter, r *http.Request) { registers := [16]int{1, 6, 56, 10, 50, 60, 10, 5, 9, 8, 10, 25, 56, 10, 20, 365} partial := 4 var wg sync.WaitGroup // 启动4个Goroutine处理分段任务 for i := 0; i < 4; i++ { start := partial * i end := partial * (i + 1) wg.Add(1) go task(registers[start:end], status, &wg) } // 单独启动Goroutine,等待所有任务完成后关闭通道 go func() { wg.Wait() close(status) }() // 统计总完成任务数 count := 0 for num := range status { count += num // 可选:实时输出进度到HTTP响应(流式输出) // fmt.Fprintf(w, "Current progress: %d/16\n", count) // w.(http.Flusher).Flush() } fmt.Fprintf(w, "Processed loop status: %d/16", count) }) fmt.Println("Server is running") if err := http.ListenAndServe(":8080", nil); err != nil { log.Fatal(err) } }
修复细节说明
- 修正参数类型:
匹配切片类型与通道读写权限,避免编译错误与逻辑冲突。 - 修复切片分段:
用start和end变量确保每个分段包含4个元素,覆盖全部16个任务。 - 引入WaitGroup同步:
用sync.WaitGroup跟踪所有Goroutine状态,确保主逻辑在所有任务完成后再统计结果;单独启动Goroutine关闭通道,避免主逻辑阻塞。 - 优化通道接收逻辑:
用for num := range status遍历通道,确保接收所有任务结果,统计准确。若需要实时展示进度,可开启注释部分的流式输出代码。 - 调整通道缓冲:
缓冲设为16,确保所有任务结果能无阻塞写入,避免Goroutine阻塞。
内容的提问来源于stack exchange,提问作者ReZ
相关产品推荐
相关产品推荐

