在 Go 中读取命名管道时使用率 100% CPU

100% CPU usage when reading named pipe in Go

我有一个简短的 Go 程序,它从命名管道读取数据,并在外部进程写入管道时处理每一行。命名管道是在程序运行之前使用 mkfifo.

创建的

在等待来自命名管道的新行时,该进程占用了 100% 的 CPU,即使它没有进行任何处理也是如此。 Ubuntu 14.04 运行。有什么想法吗?

c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)

awaitingExit := false

var wg sync.WaitGroup

go func() {
    for sig := range c {
        awaitingExit = true

        // wait for goroutines to finish processing new lines
        wg.Wait()
        os.Exit(1)
    }
}()

file, err := os.OpenFile("file.fifo", os.O_RDONLY, os.ModeNamedPipe)

defer file.Close()

if err != nil {
    log.Fatal(err)
}

reader := bufio.NewReader(file)

// infinite loop
for {
    line, _, _ := reader.ReadLine()

    // stop handling new lines if we're waiting to exit
    if !awaitingExit && len(line) > 0 {
        wg.Add(1)

        go func(uploadLog string) {
            defer wg.Done()
            handleNewLine(uploadLog)
        }(string(line))
    }
}

func handleNewLine(line string) {
    ....
}

你的"infinite loop"真的是无限:你永远不会退出或跳出它。

它包含一个 if:

// stop handling new lines if we're waiting to exit
if !awaitingExit && len(line) > 0 {
    // code omitted
}

但是如果条件为假,你仍然没有脱离for循环,只是继续另一个迭代。一旦你到达 reader 的末尾,这个循环将消耗 100% 的核心,因为在那之后它不会等待任何试图读取的东西(这将立即 return EOF)并检查 awaitExit 变量并再次执行这两个步骤。

您需要在 for 循环中添加条件以在某个时候退出,或者使用 break 语句来中断它。

改变 for 循环条件:

for !awaitingExit {
}

break 语句更改 for

for {
    if awaitingExit {
        break
    }
    // code omitted
}

注意:如果 awaitingExit 变量被另一个 goroutine 更改,你需要适当的同步,或者更好的是,使用通道作为退出信号。