Go语言嵌套循环并发处理像素时遭遇死锁问题排查
问题分析与解决方案
你的代码触发死锁的核心原因是通道c从未被关闭。主goroutine在执行for p := range c时,会持续等待通道的输入;当所有处理像素的goroutine执行完毕后,没有任何代码去关闭通道,主goroutine会永远阻塞在这里,最终导致所有goroutine都进入休眠状态,触发死锁错误。
修复方案(使用sync.WaitGroup)
通过sync.WaitGroup跟踪所有工作goroutine的执行状态,待全部完成后关闭通道,让主goroutine的range循环正常退出。
修改后的代码:
import "sync" func FindEdgesV2(img image.Image) PointSet { res := make(PointSet) c := make(chan Point) bounds := img.Bounds() width, height := bounds.Max.X, bounds.Max.Y size := width*height - width*2 - height*2 + 4 px := 0 bar := CreateBar(size, "reading pixels...") var wg sync.WaitGroup for y := 1; y < height-1; y++ { for x := 1; x < width-1; x++ { wg.Add(1) go func(x, y int, c chan Point) { defer wg.Done() gradient := [3][3]int{} for i := 0; i < 3; i++ { for j := 0; j < 3; j++ { // 修正笔误:原代码中y坐标写成了y-1+i,应该是y-1+j gradient[i][j] = int(Luminance(img.At(x-1+i, y-1+j))) } } gx, gy := 0, 0 for i := 0; i < 3; i++ { for j := 0; j < 3; j++ { gx += gradient[i][j] * horizontal[i][j] gy += gradient[i][j] * vertical[i][j] } } colorCode := int(math.Sqrt(float64(gx*gx + gy*gy))) if colorCode > 80 { c <- Point{x, y} } }(x, y, c) px++ bar.Set(px) } } // 单独启动goroutine,等待所有工作goroutine完成后关闭通道 go func() { wg.Wait() close(c) }() for p := range c { res[p] = true } return res }
额外说明
- 代码中修正了一个笔误:原代码取y坐标时用了
y-1+i,这会导致j循环时y坐标始终和i绑定,取到错误的像素,改为y-1+j才符合3x3梯度矩阵的取数逻辑。 - 不推荐通过计数接收元素的方式替代关闭通道,因为部分goroutine可能不会向通道发送数据(比如
colorCode <=80的情况),这种方式容易导致计数错误,引发新的阻塞问题。
内容的提问来源于stack exchange,提问作者ZeroTerabytes
相关产品推荐
相关产品推荐

