英文:
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 (
"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()
}
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
}
}()
<-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 &readerCtx{ctx: ctx, r: r}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论