英文:
io.Pipe and deadlock when trying to write/read
问题
我试图理解这段代码的基本逻辑已经几个小时了,但没有进展。下面的代码在第一次迭代后返回死锁。如果在io.Copy
之前关闭写入器(writer),那么死锁就会消失,但是什么都不会打印出来(因为在读取之前关闭了管道的写入端)。
func main() {
reader, writer := io.Pipe()
c := make(chan string)
go func() {
for i := 0; i < 5; i++ {
text := fmt.Sprintf("hello %vth time", i+1)
c <- text
}
close(c)
}()
for msg := range c {
msg = fmt.Sprintf("\nreceived from channel -> %v\n", msg)
go fmt.Fprint(writer, msg)
io.Copy(os.Stdout, reader)
writer.Close()
}
}
运行代码后出现以下错误:
> received from channel -> hello 1th time fatal error: all goroutines
> are asleep - deadlock!
>
> goroutine 1 [select]: io.(*pipe).read(0xc000130120, {0xc00013e000,
> 0x8000, 0xc00011e001?})
> /usr/lib/go/src/io/pipe.go:57 +0xb1 io.(*PipeReader).Read(0x0?, {0xc00013e000?, 0xc00011e050?, 0x10?})
> /usr/lib/go/src/io/pipe.go:136 +0x25 io.copyBuffer({0x4bde98, 0xc00011e050}, {0x4bddb8, 0xc00012e018}, {0x0, 0x0, 0x0})
> /usr/lib/go/src/io/io.go:427 +0x1b2 io.Copy(...)
> /usr/lib/go/src/io/io.go:386 os.genericReadFrom(0x101c00002c500?, {0x4bddb8, 0xc00012e018})
> /usr/lib/go/src/os/file.go:161 +0x67 os.(*File).ReadFrom(0xc00012e008, {0x4bddb8, 0xc00012e018})
> /usr/lib/go/src/os/file.go:155 +0x1b0 io.copyBuffer({0x4bde38, 0xc00012e008}, {0x4bddb8, 0xc00012e018}, {0x0, 0x0, 0x0})
> /usr/lib/go/src/io/io.go:413 +0x14b io.Copy(...)
> /usr/lib/go/src/io/io.go:386 main.pipetest()
> /home/stranger/source-code/golang/ipctest/pipes/main.go:39 +0x1ae main.main()
> /home/stranger/source-code/golang/ipctest/pipes/main.go:10 +0x17
>
> goroutine 18 [chan send]: main.pipetest.func1()
> /home/stranger/source-code/golang/ipctest/pipes/main.go:29 +0x85 created by main.pipetest
> /home/stranger/source-code/golang/ipctest/pipes/main.go:26 +0x17a exit status 2
英文:
I tried to understand underlying logic for several hours but no progress. This code below returns deadlock after 1st iteration. If I close writer before io.Copy
than deadlock disappears but nothing is printed(since pipe write end is closed before read)
func main() {
reader, writer := io.Pipe()
c := make(chan string)
go func() {
for i := 0; i < 5; i++ {
text := fmt.Sprintf("hello %vth time", i+1)
c <- text
}
close(c)
}()
for msg := range c {
msg = fmt.Sprintf("\nreceived from channel -> %v\n", msg)
go fmt.Fprint(writer, msg)
io.Copy(os.Stdout, reader)
writer.Close()
}
}
and this is the error after running the code
> received from channel -> hello 1th time fatal error: all goroutines
> are asleep - deadlock!
>
> goroutine 1 [select]: io.(*pipe).read(0xc000130120, {0xc00013e000,
> 0x8000, 0xc00011e001?})
> /usr/lib/go/src/io/pipe.go:57 +0xb1 io.(*PipeReader).Read(0x0?, {0xc00013e000?, 0xc00011e050?, 0x10?})
> /usr/lib/go/src/io/pipe.go:136 +0x25 io.copyBuffer({0x4bde98, 0xc00011e050}, {0x4bddb8, 0xc00012e018}, {0x0, 0x0, 0x0})
> /usr/lib/go/src/io/io.go:427 +0x1b2 io.Copy(...)
> /usr/lib/go/src/io/io.go:386 os.genericReadFrom(0x101c00002c500?, {0x4bddb8, 0xc00012e018})
> /usr/lib/go/src/os/file.go:161 +0x67 os.(*File).ReadFrom(0xc00012e008, {0x4bddb8, 0xc00012e018})
> /usr/lib/go/src/os/file.go:155 +0x1b0 io.copyBuffer({0x4bde38, 0xc00012e008}, {0x4bddb8, 0xc00012e018}, {0x0, 0x0, 0x0})
> /usr/lib/go/src/io/io.go:413 +0x14b io.Copy(...)
> /usr/lib/go/src/io/io.go:386 main.pipetest()
> /home/stranger/source-code/golang/ipctest/pipes/main.go:39 +0x1ae main.main()
> /home/stranger/source-code/golang/ipctest/pipes/main.go:10 +0x17
>
> goroutine 18 [chan send]: main.pipetest.func1()
> /home/stranger/source-code/golang/ipctest/pipes/main.go:29 +0x85 created by main.pipetest
> /home/stranger/source-code/golang/ipctest/pipes/main.go:26 +0x17a exit status 2
答案1
得分: 1
io.Copy
会一直尝试复制,直到读取器达到EOF(在这种情况下,当管道关闭时)。由于您在io.Copy
结束之后调用writer.Close()
,io.Copy
将永远不会看到EOF,并且会一直挂起。
您的代码的另一个问题是您尝试多次关闭管道(每次循环代码重复时)。通常,可关闭的对象应该只关闭一次,并且在关闭后被认为不可用。如果您需要重新使用它们,应该创建一个新实例。
以下是您代码的修订版本:
func main() {
c := make(chan string)
go func() {
for i := 0; i < 5; i++ {
text := fmt.Sprintf("hello %vth time", i+1)
c <- text
}
close(c)
}()
for msg := range c {
msg = fmt.Sprintf("\nreceived from channel -> %v\n", msg)
// 为此消息创建一个新的管道。
reader, writer := io.Pipe()
go func() {
fmt.Fprint(writer, msg)
// 写入消息后关闭管道。
writer.Close()
}()
io.Copy(os.Stdout, reader)
}
}
英文:
io.Copy
keeps trying to copy until reader reaches EOF (in this case, when the pipe is closed). Since you call writer.Close()
after io.Copy
ends, io.Copy
will never see that EOF, and hangs forever.
The other problem with your code is that you're trying to close the pipe multiple times (each time the loop code repeats). In general Close
able objects should only be closed once, and are assumed to be un-usable after being Close
d. If you need to re-use them, you should create a new instance.
Here's a working revision of your code:
func main() {
c := make(chan string)
go func() {
for i := 0; i < 5; i++ {
text := fmt.Sprintf("hello %vth time", i+1)
c <- text
}
close(c)
}()
for msg := range c {
msg = fmt.Sprintf("\nreceived from channel -> %v\n", msg)
// Create a new pipe for this message.
reader, writer := io.Pipe()
go func() {
fmt.Fprint(writer, msg)
// Close the pipe after writing the message.
writer.Close()
}()
io.Copy(os.Stdout, reader)
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论