You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go协程代码触发Deadlock,请求协助定位问题原因

Go Goroutine 管道死锁问题排查与修复

问题代码

package main

import "fmt"

func main() {
    // create & consume channel
    counter := make(chan int)
    even := make(chan int)
    odd := make(chan int)
    square := make(chan int)
    merge := make(chan int)
    out := make(chan struct{})

    //GoRoutines
    go counterFn(counter)
    go squarerFn(counter, square)
    go counterSplit(counter, even, odd)
    go merger(square, odd, merge) // 此处参数传递错误
    go printOut(merge, out)
    <-out
}

func counterFn(counter chan int) {
    for i := 0; i < 5; i++ {
        counter <- i
    }
    close(counter)
}

func squarerFn(counter chan int, square chan int) {
    for i := range counter {
        square <- i * i
    }
    close(square)
}

func counterSplit(counter chan int, even chan int, odd chan int) {
    for i := range counter {
        if i%2 == 0 {
            even <- i
        } else {
            odd <- i
        }
    }
    close(even)
    close(odd)
}

func merger(even chan int, odd chan int, merge chan int) {
    i := 0
    for {
        fmt.Printf("%d \n", i)
        a, ok := <-even
        if !ok {
            i++
        } else {
            merge <- a
        }
        a, ok = <-odd
        if !ok {
            i++
        } else {
            merge <- a
        }
        if i == 2 {
            break
        }
    }
    close(merge)
}

func printOut(merge chan int, out chan struct{}) {
    for i := range merge {
        fmt.Print(i)
    }
    close(out)
}

错误输出

go run channel_pipelineAdv.go
0 
0 , 0
1
0 , 9
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
        D:/channel_pipelineAdv.go:20 +0x233

goroutine 7 [chan send]:
main.squarerFn(0x0?, 0x0?)
        D:/channel_pipelineAdv.go:32 +0x45
created by main.main in goroutine 1
        D:/channel_pipelineAdv.go:16 +0x114

goroutine 8 [chan send]:
main.counterSplit(0x0?, 0x0?, 0x0?)
        D:/channel_pipelineAdv.go:40 +0x5e
created by main.main in goroutine 1
        D:/channel_pipelineAdv.go:17 +0x179

goroutine 9 [chan receive]:
main.merger(0x0?, 0x0?, 0x0?)
        D:/channel_pipelineAdv.go:59 +0xca
created by main.main in goroutine 1
        D:/channel_pipelineAdv.go:18 +0x1d9

goroutine 10 [chan receive]:
main.printOut(0x0?, 0x0?)
        D:/channel_pipelineAdv.go:73 +0x77
created by main.main in goroutine 1
        D:/channel_pipelineAdv.go:19 +0x227
exit status 2

问题定位

  1. 参数传递错误:main中调用merger时,错误地将square通道作为第一个参数传入,而merger函数定义的第一个参数是even通道。这导致:

    • even通道的数据没有任何goroutine接收,counterSplit在向even发送偶数时会永久阻塞
    • merger一直在读取square和odd通道,但counterSplit阻塞后无法向odd发送更多数据,merger读取odd时会阻塞
  2. 无缓冲通道阻塞连锁反应:所有通道都是无缓冲的,发送操作必须等待接收操作完成。当counterSplit阻塞在even发送时,无法继续接收counter通道的数据,导致counterFn后续的发送操作也阻塞;同时squarerFn在向square发送数据时,因merger阻塞在odd读取,无法接收square的数据,也会进入阻塞状态。最终所有goroutine都陷入等待,触发死锁。

  3. merger循环逻辑缺陷:merger的循环采用顺序读取even和odd的方式,若其中一个通道无数据且未关闭,会直接阻塞。这种方式无法正确处理两个通道的异步数据。

修复方案

  1. 修正参数传递:将merger的第一个参数改为even通道,确保counterSplit发送的偶数能被正常接收。
  2. 优化merger循环逻辑:使用select语句同时监听两个通道,配合通道关闭状态判断,确保所有数据被读取完成后再退出循环。

修正后的完整代码

package main

import "fmt"

func main() {
    counter := make(chan int)
    even := make(chan int)
    odd := make(chan int)
    square := make(chan int)
    merge := make(chan int)
    out := make(chan struct{})

    go counterFn(counter)
    go squarerFn(counter, square)
    go counterSplit(counter, even, odd)
    go merger(even, odd, merge) // 修正参数:传入even而非square
    go printOut(merge, out)
    <-out
}

func counterFn(counter chan int) {
    for i := 0; i < 5; i++ {
        counter <- i
    }
    close(counter)
}

func squarerFn(counter chan int, square chan int) {
    for i := range counter {
        square <- i * i
    }
    close(square)
}

func counterSplit(counter chan int, even chan int, odd chan int) {
    for i := range counter {
        if i%2 == 0 {
            even <- i
        } else {
            odd <- i
        }
    }
    close(even)
    close(odd)
}

func merger(even chan int, odd chan int, merge chan int) {
    evenClosed := false
    oddClosed := false

    for !evenClosed || !oddClosed {
        select {
        case val, ok := <-even:
            if ok {
                merge <- val
            } else {
                evenClosed = true
            }
        case val, ok := <-odd:
            if ok {
                merge <- val
            } else {
                oddClosed = true
            }
        }
    }
    close(merge)
}

func printOut(merge chan int, out chan struct{}) {
    for i := range merge {
        fmt.Printf("%d ", i)
    }
    close(out)
}

说明

修正后,counterSplit发送的偶数能被merger正常接收,merger通过select异步处理两个通道的数据,所有goroutine都能正常执行完毕,不会触发死锁。

内容的提问来源于stack exchange,提问作者Monicka Akilan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 06:55:23