golang:`os.Stdin.Read` 不处理由唯一 EOF 组成的输入?
golang: `os.Stdin.Read` doesn't handle input consisting of a sole EOF?
我是writing一只猫,接收到第一个字节就超时了。我让它工作,除了它不能处理 echo -n
:
❯ echo -n | time possiblycat 1000 # 1000 is the timeout in milliseconds
possiblycat 1000 0.00s user 0.00s system 0% cpu 1.008 total; max RSS 1864
cat
本身没有问题;它注意到 EOF 并立即退出:
❯ echo -n | time cat
cat 0.00s user 0.00s system 71% cpu 0.003 total; max RSS 664
这是possiblycat
的全部来源:
package main
import (
"io"
"io/ioutil"
"os"
"strconv"
"time"
)
func main() {
wait := 10
if len(os.Args) >= 2 {
waitDummy, err := strconv.Atoi(os.Args[1])
if err != nil {
panic(err)
}
wait = waitDummy
}
b := make(chan byte, 1)
go scan(b)
select {
case res := <-b:
inBytes, err := ioutil.ReadAll(os.Stdin)
if err != nil {
panic(err)
}
stdin := append([]byte{res}, inBytes...)
_, err2 := os.Stdout.Write(stdin)
if err2 != nil {
panic(err2)
}
case <-time.After(time.Duration(wait) * time.Millisecond):
os.Exit(1)
}
}
func scan(out chan byte) {
var b []byte = make([]byte, 1)
_, err := os.Stdin.Read(b)
if err == io.EOF {
return
} else if err != nil {
panic(err)
}
out <- b[0]
}
相关:
当 os.Stdin.Read
returns EOF
时,你退出 scan
函数,它在它自己的 goroutine 中 运行。
但是,没有做任何事情来告诉主 goroutine 所有输入都已处理。它正在等待通道 b
上的数据或超时。由于 b
上没有数据,达到超时。
为了正确处理这个问题,err == io.EOF
案例应该向主 goroutine 发出没有更多工作要做的信号。一个常见的模式(但肯定不是唯一的)是有一个 done
通道指示所有工作已完成。
done := make(chan bool, 1)
go scan(b, done)
select {
case res := <-b:
...
case <-done:
os.Exit(1)
case <-time.After(time.Duration(wait) * time.Millisecond):
os.Exit(1)
}
}
func scan(out chan byte, done chan bool) {
var b []byte = make([]byte, 1)
_, err := os.Stdin.Read(b)
if err == io.EOF {
fmt.Println("got EOF, exiting")
done <- true
return
} else if err != nil {
...
}
另一种(更简单的)替代方法是在完成后简单地关闭数据通道:
func scan(out chan byte) {
var b []byte = make([]byte, 1)
_, err := os.Stdin.Read(b)
if err == io.EOF {
fmt.Println("got EOF, exiting")
close(out)
return
} else if err != nil {
panic(err)
}
out <- b[0]
}
我是writing一只猫,接收到第一个字节就超时了。我让它工作,除了它不能处理 echo -n
:
❯ echo -n | time possiblycat 1000 # 1000 is the timeout in milliseconds
possiblycat 1000 0.00s user 0.00s system 0% cpu 1.008 total; max RSS 1864
cat
本身没有问题;它注意到 EOF 并立即退出:
❯ echo -n | time cat
cat 0.00s user 0.00s system 71% cpu 0.003 total; max RSS 664
这是possiblycat
的全部来源:
package main
import (
"io"
"io/ioutil"
"os"
"strconv"
"time"
)
func main() {
wait := 10
if len(os.Args) >= 2 {
waitDummy, err := strconv.Atoi(os.Args[1])
if err != nil {
panic(err)
}
wait = waitDummy
}
b := make(chan byte, 1)
go scan(b)
select {
case res := <-b:
inBytes, err := ioutil.ReadAll(os.Stdin)
if err != nil {
panic(err)
}
stdin := append([]byte{res}, inBytes...)
_, err2 := os.Stdout.Write(stdin)
if err2 != nil {
panic(err2)
}
case <-time.After(time.Duration(wait) * time.Millisecond):
os.Exit(1)
}
}
func scan(out chan byte) {
var b []byte = make([]byte, 1)
_, err := os.Stdin.Read(b)
if err == io.EOF {
return
} else if err != nil {
panic(err)
}
out <- b[0]
}
相关:
当 os.Stdin.Read
returns EOF
时,你退出 scan
函数,它在它自己的 goroutine 中 运行。
但是,没有做任何事情来告诉主 goroutine 所有输入都已处理。它正在等待通道 b
上的数据或超时。由于 b
上没有数据,达到超时。
为了正确处理这个问题,err == io.EOF
案例应该向主 goroutine 发出没有更多工作要做的信号。一个常见的模式(但肯定不是唯一的)是有一个 done
通道指示所有工作已完成。
done := make(chan bool, 1)
go scan(b, done)
select {
case res := <-b:
...
case <-done:
os.Exit(1)
case <-time.After(time.Duration(wait) * time.Millisecond):
os.Exit(1)
}
}
func scan(out chan byte, done chan bool) {
var b []byte = make([]byte, 1)
_, err := os.Stdin.Read(b)
if err == io.EOF {
fmt.Println("got EOF, exiting")
done <- true
return
} else if err != nil {
...
}
另一种(更简单的)替代方法是在完成后简单地关闭数据通道:
func scan(out chan byte) {
var b []byte = make([]byte, 1)
_, err := os.Stdin.Read(b)
if err == io.EOF {
fmt.Println("got EOF, exiting")
close(out)
return
} else if err != nil {
panic(err)
}
out <- b[0]
}