我正在产生一些goroutines,并希望给他们一个通道来发回错误。在父级goroutine中,我select
第一个错误并返回该错误,或wg.Done()
条件,它与关闭done
频道同步。
errc
的结束被推迟以避免goroutine泄漏;但它会引起竞争。
package main
import (
"log"
"sync"
"time"
)
func f(ch chan<- bool, wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(1 * time.Second)
log.Println("f sending a value")
ch <- true
log.Println("f sent a value")
}
func g(ch chan<- bool, wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(2 * time.Second)
log.Println("g sending a value")
ch <- true
log.Println("g sent a value")
}
func main() {
var wg sync.WaitGroup
ch := make(chan bool)
bufc := make(chan bool, 2)
defer func() {
log.Println("Closing bufc")
close(bufc)
log.Println("Closed bufc")
time.Sleep(5 * time.Second)
}()
wg.Add(2)
go f(bufc, &wg)
go g(bufc, &wg)
go func() {
wg.Wait()
close(ch)
}()
select {
case done, ok := <-bufc:
log.Printf("bufc closed: %v %v", done, ok)
case <-ch:
log.Println("ch was closed")
}
}
结果:
❗ ~/c/scrap
(i) go run test.go
2018/05/01 20:28:03 f sending a value
2018/05/01 20:28:03 f sent a value
2018/05/01 20:28:03 bufc closed: true true
2018/05/01 20:28:03 Closing bufc
2018/05/01 20:28:03 Closed bufc
2018/05/01 20:28:04 g sending a value
panic: send on closed channel
goroutine 19 [running]:
main.g(0xc42009c000, 0xc42008a010)
/Users/yangmillstheory/code/scrap/test.go:23 +0xb2
created by main.main
/Users/yangmillstheory/code/scrap/test.go:42 +0x11e
exit status 2
有没有办法在不引起恐慌的情况下正确清理errc
频道?我甚至需要关闭errc
吗?鉴于它是缓冲的,该频道上的发送者不会阻止,所以我猜答案是否定的?
答案 0 :(得分:2)
您的错误已经足够清楚了 - 因为{{1} bufc
可以向errc
发送值,所以g
(我假设您称之为select
)的频道已关闭}}语句只从bufc
收到一次,并且由延迟关闭。不必推迟结束bufc
,您必须进行一些同步,可能使用sync.WaitGroup
确保在关闭之前发送所有值,例如只需移动{{1} } {到close(bufc)
之后:
wg.Wait()
在您的情况下,go func() {
wg.Wait()
close(ch)
close(bufc)
}()
已缓存,您不必关闭它,因为它在接收端没有阻止,但是一旦您有两个以上的goroutines发送给您&#39;我仍然需要关闭它才能正确发出信号。
答案 1 :(得分:0)
我最终得到了以下实现,它消耗了缓冲的通道bufc
并在所有情况下都能正常工作。
var (
err error
wg sync.WaitGroup
)
ch := make(chan bool)
bufc := make(chan error, 2)
wg.Add(2)
go f(bufc, &wg)
go g(bufc, &wg)
go func() {
wg.Wait()
close(ch)
close(bufc)
}()
<-ch
log.Println("Goroutines are done")
for err = range bufc {
log.Printf("Got an error: %v", err)
}
log.Println("Returning.")
return err