当使用Go语言读取命名管道时,CPU使用率达到100%。

huangapple go评论91阅读模式
英文:

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

        // 等待goroutine完成对新行的处理
        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)

// 无限循环
for {
    line, _, _ := reader.ReadLine()

    // 如果正在等待退出,则停止处理新行
    if !awaitingExit && len(line) > 0 {
        wg.Add(1)

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

func handleNewLine(line string) {
    // 处理新行的代码
    ....
}

这是你要翻译的内容。

英文:

I have a short Go program which reads from a named pipe and processes each line as an external process writes to the pipe. The named pipe is created before the program runs using mkfifo.

The process is taking up 100% of the CPU when waiting for a new line from the named pipe, even when it's not doing any processing. It's running on Ubuntu 14.04. Any ideas?

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) {
    ....
}

答案1

得分: 1

你的“无限循环”真的是“无限”的:你从未退出或跳出它。

它包含一个if语句:

// 如果我们正在等待退出,则停止处理新行
if !awaitingExit && len(line) > 0 {
    // 代码省略
}

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

你需要在for循环中添加条件以便在某个时刻退出,或者使用break语句跳出循环。

带有条件的修改后的for循环:

for !awaitingExit {
}

带有break语句的修改后的for循环:

for {
    if awaitingExit {
        break
    }
    // 代码省略
}

注意:如果awaitingExit变量由另一个goroutine更改,你需要适当的同步,或者更好地使用通道进行退出信号传递。

英文:

Your "infinite loop" is really infinite: you never exit it or jump out of it.

It contains an if:

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

But if the condition is false, you're still not getting out of the for loop, just continue with another iteration. Once you reach the end of reader, this loop will consume 100% of a core because after that it will not wait for anything just trying to read (which will immediately return EOF) and checking the awaitExit variable and doing these 2 steps again.

You either need to add condition to the for loop to exit sometime, or use a break statement to break out of it.

Altered for loop with a condition:

for !awaitingExit {
}

Altered for with a break statement:

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

Note: if awaitingExit variable is changed by another goroutine, you need proper synchronization, or better, use channels for exit signalling.

huangapple
  • 本文由 发表于 2015年1月30日 23:08:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/28238767.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定