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

为何Go语言程序中readStdIn函数始终无法执行完成?

问题分析:Go程序读取标准输入阻塞的原因及解决

背景与需求

原本通过curl获取端点数据并管道传入Go程序,初始读取逻辑运行正常:

reader := bufio.NewReader(os.Stdin)
var buf bytes.Buffer
line, err := reader.ReadString(`\n`)
for {
  if err != nil{
     buf.WriteString(line)
     break
   }
  buf.WriteString(line)
}
var data Memstats
err = json.Unmarshal(buf.Bytes(), &data)

最终需求是通过以下循环命令,让程序按间隔读取10个JSON数据:

for i in {1..10}; do curl localhost:6000/debug/vars | ./myprogram; sleep 1; done

将读取逻辑移到readStdIn函数后,函数始终无法执行完成,请求排查原因。

主函数代码

for{
 reader := bufio.NewReader(os.Stdin)
 h.readStdIn(reader)
 time.Sleep(1 * time.Second)
 var err error
 //检查curl是否还会发送更多数据,若无则跳出循环
 newbytes, err := reader.ReadByte()
 if err != nil{
   break
 }

}

readStdIn函数代码

func (rt *Graph)readStdIn(reader *bufio.Reader){
 var buf bytes.Buffer
 line, err := reader.ReadString('\n')
 for {
  if err != nil{
     if err == io.EOF{
        buf.WriteString(line)
        break
     }else{
       fmt.Println(err.Error())
       os.Exit(1)
     }
  }
 }
 buf.WriteString(line)
}
var data Memstats
err = json.Marshal(buf.Bytes(), &data) // 此处为笔误,应为Unmarshal
rt.Memstats = append(rt.Memstats, &record)
}

问题核心原因

  1. readStdIn函数循环逻辑错误
    函数内的for循环没有持续读取新行:第一次调用ReadString后,若没有错误,循环会无限空转——因为循环体内没有再次执行line, err := reader.ReadString('\n'),err始终为nil,永远无法触发退出条件。

  2. 对管道执行逻辑的误解
    你写的循环命令中,每次curl | ./myprogram都会启动一个全新的./myprogram进程,而非让同一个进程接收10次curl数据。也就是说,Go程序每次只需要处理单次curl的输入,处理完就可以退出,不需要在进程内循环读取。

  3. 主函数循环冗余且错误
    主函数的for循环完全没必要:单次进程的标准输入只有一份curl数据,读取完就会收到EOF,不需要循环等待。每次循环创建新的bufio.Reader,还尝试ReadByte检查后续数据,只会导致进程阻塞。

  4. 代码低级笔误

    • json.Marshal应为json.Unmarshal,否则无法将JSON字节反序列化为结构体;
    • &record未定义,实际应为&data。

修复方案

方案1:适配现有命令逻辑(单次进程处理单次输入)

修改Go程序,去掉主函数的循环,只处理一次标准输入即可:

简化后的主函数:

func main() {
    h := &Graph{}
    reader := bufio.NewReader(os.Stdin)
    h.readStdIn(reader)
    // 可在此处添加数据输出/存储逻辑
}

修复后的readStdIn函数:

func (rt *Graph) readStdIn(reader *bufio.Reader) {
    var buf bytes.Buffer
    for {
        line, err := reader.ReadString('\n')
        if err != nil {
            if err == io.EOF {
                buf.WriteString(line)
                break
            }
            fmt.Println(err.Error())
            os.Exit(1)
        }
        buf.WriteString(line)
    }

    var data Memstats
    err := json.Unmarshal(buf.Bytes(), &data)
    if err != nil {
        fmt.Println("反序列化失败:", err)
        os.Exit(1)
    }
    rt.Memstats = append(rt.Memstats, &data)
}

此时你的循环命令会正常执行10次,每次启动新的程序实例处理单次curl输入。

方案2:单个进程接收多次输入(修改命令逻辑)

若希望单个Go进程接收10次curl数据,需修改命令让所有curl输出管道到同一个进程:

(for i in {1..10}; do curl localhost:6000/debug/vars; sleep 1; done) | ./myprogram

此时Go程序需循环读取完整JSON,推荐用json.Decoder直接读取,无需手动拼接缓冲区:

func main() {
    h := &Graph{}
    reader := bufio.NewReader(os.Stdin)
    dec := json.NewDecoder(reader)
    
    for {
        var data Memstats
        err := dec.Decode(&data)
        if err == io.EOF {
            break
        }
        if err != nil {
            fmt.Println("反序列化失败:", err)
            os.Exit(1)
        }
        rt.Memstats = append(rt.Memstats, &data)
    }
}

json.Decoder可自动处理JSON的边界,即使JSON跨多行也能正确读取。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 19:20:22