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

