为什么 Go 会在写入已关闭的通道时出现恐慌?
Why does Go panic on writing to a closed channel?
为什么 Go 在写入已关闭的通道时会出现恐慌?
虽然可以使用 value, ok := <-channel
习惯用法从频道读取,因此可以测试 ok 结果是否命中关闭的频道:
// reading from closed channel
package main
import "fmt"
func main() {
ch := make(chan int, 1)
ch <- 2
close(ch)
read(ch)
read(ch)
read(ch)
}
func read(ch <-chan int) {
i,ok := <- ch
if !ok {
fmt.Printf("channel is closed\n")
return
}
fmt.Printf("read %d from channel\n", i)
}
输出:
read 2 from channel
channel is closed
channel is closed
运行 "reading from closed channel" 在 Playground
写入可能已关闭的通道更加复杂,因为如果您只是在通道关闭时尝试写入,Go 会 panic:
//writing to closed channel
package main
import (
"fmt"
)
func main() {
output := make(chan int, 1) // create channel
write(output, 2)
close(output) // close channel
write(output, 3)
write(output, 4)
}
// how to write on possibly closed channel
func write(out chan int, i int) (err error) {
defer func() {
// recover from panic caused by writing to a closed channel
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
fmt.Printf("write: error writing %d on channel: %v\n", i, err)
return
}
fmt.Printf("write: wrote %d on channel\n", i)
}()
out <- i // write on possibly closed channel
return err
}
输出:
write: wrote 2 on channel
write: error writing 3 on channel: send on closed channel
write: error writing 4 on channel: send on closed channel
运行 "writing to closed channel" 在 Playground
据我所知,没有比写入可能已关闭的通道而不惊慌的更简单的习惯用法了。为什么不?这种读写不对称行为背后的原因是什么?
For a channel c, the built-in function close(c) records that no more
values will be sent on the channel. It is an error if c is a
receive-only channel. Sending to or closing a closed channel causes a
run-time panic. Closing the nil channel also causes a run-time panic.
After calling close, and after any previously sent values have been
received, receive operations will return the zero value for the
channel's type without blocking. The multi-valued receive operation
returns a received value along with an indication of whether the
channel is closed.
如果你写到一个关闭的频道,你的程序会崩溃。如果你真的想这样做,你可能会 catch this error with recover,但是如果你不知道你正在写入的通道是否打开通常是程序中存在错误的迹象。
部分引述:
Here is a motivation:
A channel "close" is really just a send of a special value on a
channel. It is a special value that promises that no more values will
be sent. Attempting to send a value on a channel after it has been
closed will panic, since actually sending the value would violate the
guarantee provided by close. Since a close is just a special kind of
send, it is also not permitted after the channel has been closed.
Here is another:
The only use of channel close is to signal to the reader that there
are no more values to come. That only makes sense when there is a
single source of values, or when multiple sources coordinate. There
is no reasonable program in which multiple goroutines close a channel
without communicating. That would imply that multiple goroutines
would know that there are no more values to send--how could they
determine that if they don't communicate?
(Ian Lance Taylor)
--
Here is another:
Closing a channel releases it as a resource. It makes no more sense to
close a channel multiple times than it makes to close a file
descriptor multiple times, or free a block of allocated memory
multiple times. Such actions imply the code is broken, which is why
closing a closed channel triggers a panic.
(Rob Pike)
--
为什么 Go 在写入已关闭的通道时会出现恐慌?
虽然可以使用 value, ok := <-channel
习惯用法从频道读取,因此可以测试 ok 结果是否命中关闭的频道:
// reading from closed channel
package main
import "fmt"
func main() {
ch := make(chan int, 1)
ch <- 2
close(ch)
read(ch)
read(ch)
read(ch)
}
func read(ch <-chan int) {
i,ok := <- ch
if !ok {
fmt.Printf("channel is closed\n")
return
}
fmt.Printf("read %d from channel\n", i)
}
输出:
read 2 from channel
channel is closed
channel is closed
运行 "reading from closed channel" 在 Playground
写入可能已关闭的通道更加复杂,因为如果您只是在通道关闭时尝试写入,Go 会 panic:
//writing to closed channel
package main
import (
"fmt"
)
func main() {
output := make(chan int, 1) // create channel
write(output, 2)
close(output) // close channel
write(output, 3)
write(output, 4)
}
// how to write on possibly closed channel
func write(out chan int, i int) (err error) {
defer func() {
// recover from panic caused by writing to a closed channel
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
fmt.Printf("write: error writing %d on channel: %v\n", i, err)
return
}
fmt.Printf("write: wrote %d on channel\n", i)
}()
out <- i // write on possibly closed channel
return err
}
输出:
write: wrote 2 on channel
write: error writing 3 on channel: send on closed channel
write: error writing 4 on channel: send on closed channel
运行 "writing to closed channel" 在 Playground
据我所知,没有比写入可能已关闭的通道而不惊慌的更简单的习惯用法了。为什么不?这种读写不对称行为背后的原因是什么?
For a channel c, the built-in function close(c) records that no more values will be sent on the channel. It is an error if c is a receive-only channel. Sending to or closing a closed channel causes a run-time panic. Closing the nil channel also causes a run-time panic. After calling close, and after any previously sent values have been received, receive operations will return the zero value for the channel's type without blocking. The multi-valued receive operation returns a received value along with an indication of whether the channel is closed.
如果你写到一个关闭的频道,你的程序会崩溃。如果你真的想这样做,你可能会 catch this error with recover,但是如果你不知道你正在写入的通道是否打开通常是程序中存在错误的迹象。
部分引述:
Here is a motivation:
A channel "close" is really just a send of a special value on a channel. It is a special value that promises that no more values will be sent. Attempting to send a value on a channel after it has been closed will panic, since actually sending the value would violate the guarantee provided by close. Since a close is just a special kind of send, it is also not permitted after the channel has been closed.
Here is another:
The only use of channel close is to signal to the reader that there are no more values to come. That only makes sense when there is a single source of values, or when multiple sources coordinate. There is no reasonable program in which multiple goroutines close a channel without communicating. That would imply that multiple goroutines would know that there are no more values to send--how could they determine that if they don't communicate?
(Ian Lance Taylor)
--
Here is another:
Closing a channel releases it as a resource. It makes no more sense to close a channel multiple times than it makes to close a file descriptor multiple times, or free a block of allocated memory multiple times. Such actions imply the code is broken, which is why closing a closed channel triggers a panic.
(Rob Pike)
--