【问题标题】:Stop goroutine that is blocked gracefully停止优雅阻塞的 goroutine
【发布时间】:2021-11-08 16:06:04
【问题描述】:

我有一个 goroutine 经常被阻止读取标准输入,像这样:

func routine() {
    for {
        data := make([]byte, 8)
        os.Stdin.Read(data);
        otherChannel <-data
    }
}

例程等待通过标准输入读取 8 个字节并提供另一个通道。

我想从主线程优雅地停止这个 goroutine。但是,由于 goroutine 几乎总是会被阻止从标准输入读取,我找不到一个好的解决方案来强制它停止。我想到了类似的东西:

func routine(stopChannel chan struct{}) {
    for {
        select {
        case <-stopChannel:
            return
        default:
            data := make([]byte, 8)
            os.Stdin.Read(data);
            otherChannel <-data
        }
    }
}

但是,问题是如果在stopChannel 关闭时,stdin 中没有更多输入,goroutine 将保持阻塞状态而不返回。

有没有什么好办法让它在主线程需要的时候立即返回?

谢谢。

【问题讨论】:

  • 您不能取消从 Stdin 读取,只需让 goroutine 运行并确保它在稍后完成时不会影响程序
  • golang.org/pkg/os/#File.SetReadDeadline 这样你可能会中断你的阅读
  • @GregGiacovelli:这将在标准输入上返回 ErrNoDeadline
  • 应该可以在标准输入和消费者之间粘贴io.Pipe,然后关闭管道的一侧——这应该让消费者收到io.EOF,生产者返回io.ErrClosedPipe (所以它也可能退出)。我没有测试过,但值得研究。
  • @kostix,生产者不会被打断,因为它也会在 Read 中被阻塞。你可以设置一个 POSIX “自我管道技巧”,然后轮询实际的 fd 并关闭一个 os.Pipe,但这几乎不值得。

标签: go


【解决方案1】:

检测os.Stdin已关闭:检查os.Stdin.Read()返回的错误值。

还有一点:虽然您声明在您的情况下您将始终收到 8 个字节的块,但您仍然应该检查您是否确实收到了 8 个字节的数据。

func routine() {
    for {
        data := make([]byte, 8)
        n, err := os.Stdin.Read(data)

        // error handling : the basic thing to do is "on error, return"
        if err != nil {
            // if os.Stdin got closed, .Read() will return 'io.EOF'
            if err == io.EOF {
                log.Printf("stdin closed, exiting")
            } else {
                log.Printf("stdin: %s", err)
            }
            return
        }

        // check that 'n' is big enough :
        if n != 8 {
            log.Printf("short read: only %d byte. exiting", n)
            return  // instead of returning, you may want to keep '.Read()'ing
                    // or you may use 'io.ReadFull(os.Stdin, data)' instead of '.Read()'
        }

        // a habit to have : truncate your read buffers to 'n' after a .Read()
        otherChannel <-data[:n]
    }
}

【讨论】:

    猜你喜欢
    • 2020-04-03
    • 1970-01-01
    • 2020-10-12
    • 2022-09-26
    • 2017-09-12
    • 1970-01-01
    • 2016-02-01
    • 1970-01-01
    • 2010-11-05
    相关资源
    最近更新 更多