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

Go语言io.Pipe读写操作出现死锁问题求助

Go代码死锁分析与修复

问题原因

这段代码在第一次循环后触发死锁,核心问题出在两个地方:

  1. io.Copy的阻塞逻辑:io.Copy(os.Stdout, reader)会持续从PipeReader读取数据,直到对应的PipeWriter被关闭(触发EOF)。但原代码中writer.Close()放在io.Copy之后,第一次循环时,goroutine写完第一个消息后writer并未关闭,io.Copy读完消息后会一直阻塞等待更多数据,导致主goroutine无法进入下一次循环接收channel消息。
  2. 无缓冲channel的发送阻塞:c是无缓冲channel,发送goroutine在发送第二个消息时,会等待主goroutine接收,但主goroutine卡在io.Copy中,发送goroutine因此阻塞。最终所有goroutine都进入休眠状态,触发死锁。

原代码

func main() {
    reader, writer := io.Pipe()
    c := make(chan string)

    go func() {
        for i := 0; i < 5; i++ {
            text := fmt.Sprintf("hello %vth time", i+1)
            c <- text
        }

        close(c)
    }()

    for msg := range c {
        msg = fmt.Sprintf("\nreceived from channel -> %v\n", msg)

        go fmt.Fprint(writer, msg)
        io.Copy(os.Stdout, reader)
        writer.Close()
    }
}

死锁报错信息

received from channel -> hello 1th time fatal error: all goroutines are asleep - deadlock!

goroutine 1 [select]: io.(*pipe).read(0xc000130120, {0xc00013e000, 0x8000, 0xc00011e001?})
/usr/lib/go/src/io/pipe.go:57 +0xb1 io.(*PipeReader).Read(0x0?, {0xc00013e000?, 0xc00011e050?, 0x10?})
/usr/lib/go/src/io/pipe.go:136 +0x25 io.copyBuffer({0x4bde98, 0xc00011e050}, {0x4bddb8, 0xc00012e018}, {0x0, 0x0, 0x0})
/usr/lib/go/src/io/io.go:427 +0x1b2 io.Copy(...)
/usr/lib/go/src/io/io.go:386 os.genericReadFrom(0x101c00002c500?, {0x4bddb8, 0xc00012e018})
/usr/lib/go/src/os/file.go:161 +0x67 os.(*File).ReadFrom(0xc00012e008, {0x4bddb8, 0xc00012e018})
/usr/lib/go/src/os/file.go:155 +0x1b0 io.copyBuffer({0x4bde38, 0xc00012e008}, {0x4bddb8, 0xc00012e018}, {0x0, 0x0, 0x0})
/usr/lib/go/src/io/io.go:413 +0x14b io.Copy(...)
/usr/lib/go/src/io/io.go:386 main.pipetest()
/home/stranger/source-code/golang/ipctest/pipes/main.go:39 +0x1ae main.main()
/home/stranger/source-code/golang/ipctest/pipes/main.go:10 +0x17

goroutine 18 [chan send]: main.pipetest.func1()
/home/stranger/source-code/golang/ipctest/pipes/main.go:29 +0x85 created by main.pipetest
/home/stranger/source-code/golang/ipctest/pipes/main.go:26 +0x17a exit status 2

修复方案

调整io.Copy的执行时机,让它在后台持续读取管道数据,同时确保所有写入完成后关闭管道,避免阻塞:

import (
    "fmt"
    "io"
    "os"
    "sync"
)

func main() {
    reader, writer := io.Pipe()
    c := make(chan string)

    // 启动goroutine发送消息到channel
    go func() {
        for i := 0; i < 5; i++ {
            text := fmt.Sprintf("hello %vth time", i+1)
            c <- text
        }
        close(c)
    }()

    // 启动单独goroutine处理管道输出,用WaitGroup等待完成
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        defer wg.Done()
        io.Copy(os.Stdout, reader)
    }()

    // 遍历channel,将消息写入管道
    for msg := range c {
        msg = fmt.Sprintf("\nreceived from channel -> %v\n", msg)
        fmt.Fprint(writer, msg)
    }

    // 所有消息写入完成后关闭writer,触发reader的EOF
    writer.Close()
    // 等待io.Copy完成所有数据输出
    wg.Wait()
}

关键说明

  1. 将io.Copy放到后台goroutine,主goroutine可以继续接收channel消息,避免阻塞。
  2. 遍历完channel后再关闭writer,确保所有消息都写入管道,io.Copy会读取完所有数据后因EOF自动退出。
  3. 使用sync.WaitGroup等待io.Copy完成,保证所有数据输出到stdout后程序才退出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 00:25:19