是否可以通过关闭src来中断io.Copy?

Is it possible to interrupt io.Copy by closing src?

代码示例:

package main

import (
    "io"
    "os"
    "os/signal"
    "sync"
    "syscall"
)

func main() {
    sigintCh := make(chan os.Signal, 1)
    signal.Notify(sigintCh, syscall.SIGINT, syscall.SIGTERM)

    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        defer wg.Done()
        io.Copy(os.Stdout, os.Stdin)
    }()

    <-sigintCh

    os.Stdin.Close()

    wg.Wait()
}

如果 运行 这个示例并尝试通过 ^C 中断它等待任何输入并仅在向标准输入发送内容后停止(例如只需按 enter)。

我希望关闭 Stdin 就像发送 EOF 一样,但它不起作用。

关闭 os.Stdin 将导致 io.Copy 变为 return,并在下次读取时出现错误 file already closed(在 CTRL-C 之后,尝试按 Enter).

File.Close 文档中所述:

Close closes the File, rendering it unusable for I/O.

您不能通过关闭(或任何其他方式)从 os.Stdin 强制 EOF return。相反,您需要包装 os.Stdin 并实现您自己的 Read 有条件地 return s EOF 的方法,或者在循环中读取有限数量的字节。

您可以在此 golang-nuts 主题中看到更多讨论和可能的解决方法。

您可以中断 io.Copy 而无需 关闭源端 - 通过传递已用可取消 context.Context 概述 here.

像这样修改上面的 goroutine:

ctx, cancel := context.WithCancel(context.Background())

go func() {
    defer wg.Done()

    r := NewReader(ctx, os.Stdin) // wrap io.Reader to make it context-aware
    _, err := io.Copy(os.Stdout, r)
    if err != nil {
        // context.Canceled error if interrupted
    }
}()

<-sigintCh
cancel() // canceling context will interrupt io.Copy operation

您可以从 github.com/jbenet/go-context/io 等外部包中导入 NewReader 或从上面的博客 link 中内嵌片段:

type readerCtx struct {
    ctx context.Context
    r   io.Reader
}

func (r *readerCtx) Read(p []byte) (n int, err error) {
    if err := r.ctx.Err(); err != nil {
        return 0, err
    }
    return r.r.Read(p)
}

// NewReader gets a context-aware io.Reader.
func NewReader(ctx context.Context, r io.Reader) io.Reader {
    return &readerCtx{ctx: ctx, r: r}
}