@Golang The close() of billions of small details,
what you overlook, is often the origin of the bug
close() pre-knowledge
1. Cannot close() a channel that has been closed()
package main
func main(){
ch := make(chan int)
close(ch)
close(ch)
// output:
// panic: close of closed channel
}
2. After the channel is closed(), it cannot be written (note that it will panic: send on closed channel), but it can be read. The reading rule is to read the cache value if there is a cache value, and read zero if there is no cache value. Note: v, ok := <-ch
China ok
cannot determine when the channel is closed
package main
import "fmt"
func main() {
ch := make(chan int,3)
for i := 0; i < 3; i++ {
ch <- i
}
close(ch)
for i := 0; i < 5; i++ {
v, ok := <-ch
fmt.Println(v, ok)
}
fmt.Println("如果没有下一行就是写入失败被阻塞了啊")
ch <- 1
fmt.Println("我是下一行")
/*
output:
0 true
1 true
2 true
0 false
0 false
如果没有下一行就是写入失败被阻塞了啊
panic: send on closed channel
*/
}
Note:v, ok := <-ch
China ok
does not necessarily determine when the channel is closed
package main
import "fmt"
func main() {
ch := make(chan int, 3)
for i := 1; i < 4; i++ {
ch <- i
}
close(ch)
for i := 0; i < 4; i++ {
v, ok := <-ch
fmt.Println(v,ok)
}
/*
output
1 true
2 true
3 true
0 false
*/
}
Why ok
is it unreliable to use the mode to determine whether the channel is closed?
As output, the cache memory in the channel in the value, even if the channel is closed, ok
the value is still true
, to remember 0
and false
is one pair of Oh! When the channel is closed and there is no cache value, it ok
is false
. When using the channel at the same time, it is best to close it when it is not needed. If all goroutine
are blocked, it willpanic
Serve
The following code is provided by the brainstorming Go language group friends Tanhua month by month . He asked a question. All three coroutines have close(). Why is there no output?
panic: close of closed channel
See the comments for details.
package main
import (
"fmt"
)
func main() {
a := 0
fn := func() int {
a++
return a
}
ch := make(chan int, 1)
chh := make(chan int, 3)
for i := 0; i < 3; i++ {
go func(j int) {
for {
ch <- 1
n := fn()
if n > 100 {
// 当第一个协程进入这段代码区域时,chh最终会被关闭
// 剩下的两个协程因为chh被关闭,在写入按理来说应该会报panic: send on closed channel
// 然而并未出现,原因在于其实协程在ch <- 1 的位置被阻塞了,为何?
// ch容量为1,在第一个协程退出的时候并为将ch中的值取出,导致被阻塞
chh <- 1
close(chh)
return
}
fmt.Println("go", j, n)
<-ch
}
}(i)
}
for i := 0; i < 3; i++ {
// 那这样是不是代表这chh中只有一个值,是的!但是为何主进程没阻塞呢?
// 原因在于channle被关闭能读出0,所以这个循环在chh被关闭后马上就结束了
<-chh
}
}
Dessert
After solving the above problem month by month, Tan Hua also raised a question, how to use the channel to achieve safe exit? , In fact, the above scheme is just a prototype, just need to modify the code slightly. For the convenience of reading, I will reorganize the code while the basic disk of the above code remains unchanged.
func main() {
data := make(chan int, 100) // 用来存储0-100
code := make(chan int, 1) // 相当于ch,写入code则有执行权
count := make(chan int, 3) // 相当于chh,用于统计
for i := 0; i < 3; i++ {
go func(code chan int, data <-chan int, count chan<- int) {
for {
code <- 1 // 获得执行权
if v, ok := <-data; ok {
fmt.Println(v)
<-code //释放执行权
} else {
count <- 1 // 计数
<- code //释放执行权
return
}
}
}(code, data, count)
}
go func(data chan<- int) {
for i := 0; i < 101; i++ {
data <- i
}
close(data)
}(data)
for i := 0; i < 3; {
i = i + <-count // 统计
}
}
In fact, this implementation is similar to sync.WaitGroup.