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

使用两个fmt.Println与Go协程时出现死锁问题求助

Go协程与通道死锁问题的解决方案

你的代码出现死锁的核心原因是无缓冲通道的发送和接收是一一绑定的:

  • 第一个协程往routinemsg发送一次切片数据后,main函数里的<-routinemsg会把这个数据取走,通道内不再有剩余数据。
  • 第二个协程执行到<-routinemsg时,会一直阻塞等待数据,永远无法完成后续的append和向routinemsg2发送数据的操作。
  • main函数在等待<-routinemsg2时也会阻塞,最终整个程序陷入死锁。

场景1:只需要获取最终处理后的结果

如果不需要单独打印原始切片,直接修改main逻辑,只读取处理后的通道数据即可:

func main() {
    routinemsg := make(chan []Person)
    routinemsg2 := make(chan []Person)

    p1 := newPerson("john doe", 25)
    p2 := newPerson("dohn joe", 52)
    p3 := newPerson("bohn joo", 30)

    go func() { routinemsg <- []Person{p1, p2} }()
    go func() { routinemsg2 <- append(<-routinemsg, p3) }()
    
    // 仅读取处理后的结果,不再重复读取routinemsg
    finalSlice := <-routinemsg2
    fmt.Println("原始切片:", finalSlice[:2])
    fmt.Println("追加后的切片:", finalSlice)
}

场景2:需要同时获取原始切片和处理后的切片

如果必须分别打印两个切片,调整协程逻辑,让原始数据被正确传递,避免重复读取同一个无缓冲通道:

func main() {
    originalChan := make(chan []Person)
    processedChan := make(chan []Person)

    p1 := newPerson("john doe", 25)
    p2 := newPerson("dohn joe", 52)
    p3 := newPerson("bohn joo", 30)

    // 单个协程完成数据发送和处理,避免通道竞争
    go func() {
        original := []Person{p1, p2}
        originalChan <- original
        processedChan <- append(original, p3)
    }()

    // 按顺序接收数据,不会阻塞
    fmt.Println(<-originalChan)
    fmt.Println(<-processedChan)
}

用WaitGroup管理多协程(针对复杂场景)

如果后续需要扩展更多协程,可以用sync.WaitGroup跟踪所有协程的完成状态,避免因协程未结束导致的阻塞或资源泄漏:

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    originalChan := make(chan []Person)
    processedChan := make(chan []Person)

    p1 := newPerson("john doe", 25)
    p2 := newPerson("dohn joe", 52)
    p3 := newPerson("bohn joo", 30)

    // 标记需要等待2个协程
    wg.Add(2)

    // 协程1:发送原始切片
    go func() {
        defer wg.Done() // 协程结束时标记完成
        originalChan <- []Person{p1, p2}
    }()

    // 协程2:处理切片并发送结果
    go func() {
        defer wg.Done()
        original := <-originalChan
        processedChan <- append(original, p3)
    }()

    // 单独协程等待所有工作协程完成后关闭通道
    go func() {
        wg.Wait()
        close(originalChan)
        close(processedChan)
    }()

    // 接收并打印数据
    fmt.Println(<-originalChan)
    fmt.Println(<-processedChan)
}

WaitGroup核心用法:

  • wg.Add(n):告诉WaitGroup需要等待n个协程完成。
  • wg.Done():每个协程执行完毕时调用,减少等待计数。
  • wg.Wait():阻塞直到所有协程都调用了Done(),计数归0。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 20:55:09