首页 > 解决方案 > 停止优雅阻塞的 goroutine

问题描述

我有一个不断阻止读取标准输入的 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 将保持阻塞状态而不返回。

有没有一种好方法可以让它在主线程需要时立即返回?

谢谢。

标签: go

解决方案


检测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]
    }
}

推荐阅读