为什么当我在发件人端关闭时仍然会出现恐慌 "send on closed channel"?
Why still Go panic "send on closed channel" when I close on sender side?
我有一个 stopChan
通知发件人关闭频道和一个 sync.Once
确保只有一个发件人可以关闭频道,但我仍然 "send on closed channel" 恐慌,为什么?
func muitiSenderClose() {
const SenderNum = 3
wg := sync.WaitGroup{}
wg.Add(SenderNum)
intChan := make(chan int)
stopChan := make(chan struct{})
once := sync.Once{}
for i := 0; i < SenderNum; i++ {
go func(i int) {
defer wg.Done()
needStop := false
for {
select {
case <-stopChan:
needStop = true
case intChan <- 1:
case intChan <- 2:
case intChan <- 3:
}
if needStop {
break
}
}
once.Do(func() {
fmt.Printf("%d want to close chan\n",i)
close(intChan)
})
fmt.Println("End. [sender] %id", i)
}(i)
}
sum := 0
for e := range intChan {
fmt.Printf("Receive %d\n", e)
sum += e
if sum > 10 {
close(stopChan)
fmt.Printf("Got %d\n", sum)
break
}
}
fmt.Println("End. [receiver]")
wg.Wait()
}
go
并发很厉害。协调并发非常困难。幸运的是 go
标准库有很多工具可以帮助解决这个问题。您可能应该熟悉 context
包。
context.Context
在幕后使用 done-channels(类似于您的 stopChan
)但还有其他机制,例如取消链。它们还在整个 go
标准库中用于 http、数据库和其他 blocking-type 请求。
正如@JimB 提到的,通常在协调 producer/consumers 时很少需要 sync.Once
。 chan
s 和 sync.WaitGroup
s 通常就足够了。
无论如何,这是使用 context.Context
:
对您的代码进行的协调修复
我有一个 stopChan
通知发件人关闭频道和一个 sync.Once
确保只有一个发件人可以关闭频道,但我仍然 "send on closed channel" 恐慌,为什么?
func muitiSenderClose() {
const SenderNum = 3
wg := sync.WaitGroup{}
wg.Add(SenderNum)
intChan := make(chan int)
stopChan := make(chan struct{})
once := sync.Once{}
for i := 0; i < SenderNum; i++ {
go func(i int) {
defer wg.Done()
needStop := false
for {
select {
case <-stopChan:
needStop = true
case intChan <- 1:
case intChan <- 2:
case intChan <- 3:
}
if needStop {
break
}
}
once.Do(func() {
fmt.Printf("%d want to close chan\n",i)
close(intChan)
})
fmt.Println("End. [sender] %id", i)
}(i)
}
sum := 0
for e := range intChan {
fmt.Printf("Receive %d\n", e)
sum += e
if sum > 10 {
close(stopChan)
fmt.Printf("Got %d\n", sum)
break
}
}
fmt.Println("End. [receiver]")
wg.Wait()
}
go
并发很厉害。协调并发非常困难。幸运的是 go
标准库有很多工具可以帮助解决这个问题。您可能应该熟悉 context
包。
context.Context
在幕后使用 done-channels(类似于您的 stopChan
)但还有其他机制,例如取消链。它们还在整个 go
标准库中用于 http、数据库和其他 blocking-type 请求。
正如@JimB 提到的,通常在协调 producer/consumers 时很少需要 sync.Once
。 chan
s 和 sync.WaitGroup
s 通常就足够了。
无论如何,这是使用 context.Context
: