我知道有一个称为 SetReadDeadline
的函数,可以在套接字(conn.net)读取中设置超时,而io.read却没有。有一种方法可以启动另一个例程作为计时器来解决此问题,但是它带来了读者例程(io.read)仍然阻止的另一个问题:
func (self *TimeoutReader) Read(buf []byte) (n int, err error) {
ch := make(chan bool)
n = 0
err = nil
go func() { // this goroutime still exist even when timeout
n, err = self.reader.Read(buf)
ch <- true
}()
select {
case <-ch:
return
case <-time.After(self.timeout):
return 0, errors.New("Timeout")
}
return
}
这个问题在这篇文章中是相似的,但是答案尚不清楚。你们有什么好主意解决这个问题吗?
而不是直接在read
上设置超时,您可以在超时后close
CC_3。如https://golang.org/pkg/os/#file.close
关闭关闭文件,使其无法使用I/O。在支持setDeadline的文件上,任何待处理的I/O操作都将被取消,并立即返回错误。
这应该导致您的read
立即失败。
您的错误在这里是不同的:
当您从读者那里阅读时,您只读一次,这是错误的:
go func() {
n, err = self.reader.Read(buf) // this Read needs to be in a loop
ch <- true
}()
这是一个简单的示例(https://play.golang.org/p/2anhrbrhlrv)
buf := bytes.NewBufferString("0123456789")
r := make([]byte, 3)
n, err := buf.Read(r)
fmt.Println(string(r), n, err)
// Output: 012 3 <nil>
使用io.Reader时使用给定切片的大小。如果您会在代码中记录n个变量,则不会读取整个文件。Goroutine之外的选择语句在错误的位置。
go func() {
a := make([]byte, 1024)
for {
select {
case <-quit:
result <- []byte{}
return
default:
_, err = self.reader.Read(buf)
if err == io.EOF {
result <- a
return
}
}
}
}()
但是还有更多!您要实现io.Reader接口。调用Read()
方法直到文件结束之前,您不应该在此处启动Goroutine,因为您只会读取文件的块。此外,read()方法中的超时无济于事,因为该超时可适用于每个呼叫,而不是整个文件。
除了 @apxp关于循环读取的观点外,您还可以使用1个字节的缓冲区大小,这样您就永远不会屏蔽,只要有数据可读取。
与外部资源交互时,任何事情都可能发生。任何给定的IO.Reader实现都可以永远阻止。在这里,我会为你写一个...
type BlockingReader struct{}
func (BlockingReader) Read(b []byte) (int, error) {
<-make(chan struct{})
return 0, nil
}
请记住,任何人都可以实现接口,因此您不能做出任何假设,即它的行为将像*os.File
或任何其他标准库io.Reader
。除了上述我的asinine编码外,io.Reader
还可以合法地连接到可以永远阻止的资源。
您无法杀死Gorountines,因此,如果io.Reader真正阻止了封锁的Goroutine,则将继续消耗资源,直到您的应用程序终止为止。但是,这应该不是问题,而被阻止的goroutine不会以资源的方式消耗太多,只要您不盲目地重试封底读物,就可以很好地消耗。