通过关闭源文件,可以中断io.Copy吗?

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

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中断,它会等待任何输入,并且只有在向stdin发送一些内容后才停止(例如,只需按回车键)。

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

英文:

The sample of code:

package main

import (
	&quot;io&quot;
	&quot;os&quot;
	&quot;os/signal&quot;
	&quot;sync&quot;
	&quot;syscall&quot;
)

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)
	}()

	&lt;-sigintCh

	os.Stdin.Close()

	wg.Wait()
}

If run this sample and try to interrupt by ^C it waits for any input and stops only after sending something to stdin (e.g. just press enter).

I expect that closing Stdin will be like sending EOF, but it doesn't work.

答案1

得分: 2

关闭os.Stdin会导致io.Copy在下一次从中读取时返回错误file already closed(在按下CTRL-C后,尝试按下Enter键)。

File.Close文档中所解释的:

> Close关闭文件,使其无法进行I/O操作。

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

你可以在golang-nuts线程中看到更多讨论和可能的解决方法。

英文:

Closing os.Stdin will cause io.Copy to return with error file already closed next time it reads from it (after CTRL-C, try pressing Enter).

As explained in the File.Close docs:

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

You cannot force an EOF return from os.Stdin by closing it (or any other way). Instead, you would need to either wrap os.Stdin and implement your own Read method that conditionally returns EOF, or read a limited number of bytes in a loop.

You can see some more discussion and possible workarounds on this golang-nuts thread.

答案2

得分: 2

你可以通过使用已经包装了可取消的context.Context的逻辑来中断io.Copy操作,而无需关闭源端。具体的实现可以参考这里的链接

修改你上面的goroutine如下:

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

go func() {
    defer wg.Done()

    r := NewReader(ctx, os.Stdin) // 包装io.Reader使其具备上下文感知能力
    _, err := io.Copy(os.Stdout, r)
    if err != nil {
        // 如果被中断,会返回context.Canceled错误
    }
}()

<-sigintCh
cancel() // 取消上下文将中断io.Copy操作

你可以从外部包导入NewReader,例如github.com/jbenet/go-context/io,或者内联使用上面链接中的代码片段:

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 获取一个具备上下文感知能力的io.Reader。
func NewReader(ctx context.Context, r io.Reader) io.Reader {
    return &readerCtx{ctx: ctx, r: r}
}
英文:

You can interrupt an io.Copy without closing the source side - by passing an io.Reader that has been wrapped with logic that takes a cancelable context.Context outlined here.

Modify your above goroutine like so:

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
    }
}()

&lt;-sigintCh
cancel() // canceling context will interrupt io.Copy operation

You can import NewReader from an external package like github.com/jbenet/go-context/io or inline a snippet from the blog link above:

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 &amp;readerCtx{ctx: ctx, r: r}
}

huangapple
  • 本文由 发表于 2020年9月8日 15:54:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/63789503.html
匿名

发表评论

匿名网友

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

确定