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

Go语言中如何为循环扫描的bufio.Scanner设置超时?

Go进程监控中解决bufio.Scanner.Scan()阻塞导致无法响应终止信号的问题

问题场景

用Go启动长期运行的进程并实时监控其输出,需要支持通过信号终止进程。当前代码中,当进程无输出时,scanner.Scan()会阻塞主线程,导致无法及时响应processFinishChan的终止信号。尝试将Scan()放入goroutine,但担心外层循环会导致阻塞的goroutine堆积。

解决方案

核心思路是将输出扫描逻辑单独放到一个goroutine中,通过通道传递扫描结果,主goroutine专注监听终止信号和输出数据,既解决阻塞问题,也不会产生goroutine泄漏。

修改后的代码示例:

// 启动进程的代码...
scanner := bufio.NewScanner(stdout)
outputChan := make(chan []byte, 10) // 带缓冲避免扫描goroutine临时阻塞
errChan := make(chan error, 1)

// 单独goroutine负责扫描进程输出
go func() {
    defer close(outputChan)
    defer close(errChan)
    for scanner.Scan() {
        // 复制扫描到的字节切片,避免后续被覆盖
        data := make([]byte, len(scanner.Bytes()))
        copy(data, scanner.Bytes())
        outputChan <- data
    }
    if err := scanner.Err(); err != nil {
        errChan <- err
    }
}()

// 主循环:监听终止信号、输出数据和扫描错误
for {
    select {
    case <-processFinishChan:
        log.Println("Killing Process")
        if err := cmdObject.Process.Kill(); err != nil {
            log.Printf("Error Killing: %v", err)
        }
        return
    case data, ok := <-outputChan:
        if !ok {
            // 输出通道关闭,说明进程输出结束
            return
        }
        WSOutChanHolder.mu.Lock()
        for _, ch := range WSOutChanHolder.data {
            ch <- data
        }
        WSOutChanHolder.mu.Unlock()
    case err := <-errChan:
        log.Printf("Scanner error occurred: %v", err)
        return
    }
}

关键说明

  1. 无goroutine堆积问题:每个进程仅启动一个扫描goroutine,当收到终止信号杀死进程后,进程的stdout会被关闭,scanner.Scan()会返回false,扫描goroutine的循环结束后自动退出;即使主goroutine先返回,扫描goroutine也会因stdout关闭而终止,不会残留。
  2. 避免CPU空转:原代码的default分支会在无输出时持续循环,导致高CPU占用。改用通道接收后,主goroutine会在select处阻塞,仅在有事件时唤醒,大幅降低CPU消耗。
  3. 字节切片复制:scanner.Bytes()返回的是内部缓冲区的引用,后续扫描会覆盖该数据,因此需要复制一份再发送到通道,避免数据错乱。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 15:38:14