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

Go 是否可以通过关闭src来中断io.Copy?,go,io,Go,Io,代码示例: package main import ( "io" "os" "os/signal" "sync" "syscall" ) func main() { sigintCh := make(chan os.Signal, 1) signal.Notify(sigintCh, syscall.SIGINT, syscall.SIG

代码示例:

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()
}
主程序包
进口(
“io”
“操作系统”
“操作系统/信号”
“同步”
“系统调用”
)
func main(){
sigintCh:=make(信道操作信号,1)
signal.Notify(sigintCh、syscall.SIGINT、syscall.SIGTERM)
var wg sync.WaitGroup
工作组.添加(1)
go func(){
推迟工作组完成()
io.Copy(os.Stdout,os.Stdin)
}()

关闭
os.Stdin
将导致
io.Copy
在下次读取时返回错误
已关闭的文件
(在
CTRL-C
之后,尝试按
Enter

如文件中所述:

关闭关闭文件,使其无法用于I/O

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


您可以在此线程上看到更多的讨论和可能的解决方法。

您可以在不关闭源端的情况下中断
io.Copy
,方法是传递一个
io.Reader
,该读取器已用可取消的
context.context
逻辑包装

修改上述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

Matt Ryer讨论了这一点—无需关闭
io.Reader
—下面的详细信息。它不会中断读取调用。我不确定将问题隐藏在调用后面并“再来一次”是否是一个好主意goroutine.Infinite Read是一个奇怪的例子。这是我第一次遇到它。我认为逻辑应该清晰而简单。例如,goroutine with Read应该在应用程序退出时被忽略,或者应该有一个读取截止日期。是的-不想让答案复杂化-但是是的,你是对的,上面的内容不会抓住潜在原因的情况d永远阻塞。这是有解决办法的。当我处理使用
net.Conn
的TCP应用程序时,这不仅是一个
io.Reader
(和
io.Writer
)-它还有
setReadDailate
SetWriteDeadline
。利用这些具有上述取消逻辑的功能-将取消任何读/写-即使在事实发生之后。@AlekseyBakin有趣的是
os.Stdin
类型的
*os.File
支持setReadDailate。在net.Conn io上使用它。读卡器允许提前取消-但是,如果我尝试在*os.File上使用它,它似乎不起作用(至少在MacOS上是这样):奇怪的是,在关闭它之后是否可以重新初始化
os.Stdin
:从its开始,像这样简单地初始化
Stdin=NewFile(uintpttr(syscall.Stdin),“/dev/Stdin”)
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}
}