go channel在dave cheney的blog中讲过,能不用就不用,实际上的确,在你没法100%把控它的时候,使用的同时也就埋下了一个炸弹。但是作为go的关键特性之一,不用也略微浪费。
主要的其实就几点
- 不要close closed channel
- 不要给closed channel发消息
正确关闭示例,利用信号的方式
- 示例1
func main() {
rand.Seed(time.Now().UnixNano())
const Max = 100000
const NumSenders = 1000
dataCh := make(chan int, 100)
stopCh := make(chan struct{})
for i := 0; i < NumSenders; i++ {
go func() {
for {
select {
// for select不堵塞的等待关闭信号
case <-stopCh:
return
case dataCh <- rand.Intn(Max):
}
}
}()
}
go func() {
for value := range dataCh {
// 发送关闭信号
if value == Max-1 {
close(stopCh)
return
}
}
}()
// 超时结束main goroutine
select {
case <-time.After(time.Hour):
}
}
缺点
- 示例2
func main() {
rand.Seed(time.Now().UnixNano())
const Max = 100000
const NumReceivers = 10
const NumSenders = 1000
dataCh := make(chan int, 100)
stopCh := make(chan struct{})
// It must be a buffered channel.
toStop := make(chan string, 1)
var stoppedBy string
// moderator
go func() {
stoppedBy = <-toStop
close(stopCh)
}()
// senders
for i := 0; i < NumSenders; i++ {
go func(id string) {
for {
value := rand.Intn(Max)
if value == 0 {
select {
case toStop <- "sender#" + id:
default:
}
return
}
select {
case <- stopCh:
return
case dataCh <- value:
}
}
}(strconv.Itoa(i))
}
// receivers
for i := 0; i < NumReceivers; i++ {
go func(id string) {
for {
select {
case <- stopCh:
return
case value := <-dataCh:
if value == Max-1 {
select {
case toStop <- "receiver#" + id:
default:
}
return
}
fmt.Println(value)
}
}
}(strconv.Itoa(i))
}
select {
case <- time.After(time.Hour):
}
}
第二个例子就是利用了一个中间人的角色,这里设置成缓冲的toStop原因就是避免丢失关闭的信息,简单实验倒是没什么太大影响。
原则
-
don't close a channel from the receiver side and don't close a channel if the channel has multiple concurrent senders.
-
don't close (or send values to) closed channels.