Sync.WaitGroup,为什么在 goroutine 中更接近
Sync.WaitGroup, why closer in a goroutine
下面是Go编程书上的示例代码。我不明白为什么 closer 需要成为它自己的 goroutine。我试图将 closer 移动到 main 中,但它崩溃了。有人可以解释为什么 closer 需要在单独的 goroutine 中吗?
谢谢!
func makeThumbnails(filenames <-chan string, result chan<- int64) int64 {
sizes := make(chan int64)
var wg sync.WaitGroup
for f := range filenames {
wg.Add(1)
go func(f string) {
defer wg.Done()
sizes <- int64(len(f))
}(f)
}
// **closer**, why this guy needs to be in a goroutine???
go func() {
wg.Wait()
close(sizes)
}()
var total int64
for size := range sizes {
total += size
}
result <- total
return total
}
问题是 sizes
不是缓冲的 chan
,因此在需要读取 sizes
之前,只有一个匿名 goroutines 可以实际完成。这使得 wg.Wait()
永远等待(因为下一个 goroutine 在 sizes <-
上阻塞并且不能 defer wg.Done()
)和死锁。
通过将 closer 放入单独的 goroutine 中,它可以在准备好时关闭 sizes
chan,并在两者之间从 sizes
开始处理。归根结底,这是 goroutine 的一个很好的用途——启动后忘记关闭!
为了让这段代码在没有 closer goroutine 的情况下工作,你可以简单地将 sizes
初始化为一个缓冲区 >= filenames
.
的长度的缓冲 chan
func makeThumbnails(filenames <-chan string, result chan<- int64) int64 {
sizes := make(chan int64, 10) // buffered channel, now!
// if filenames sends more than 10 strings, though, we're in trouble!!
var wg sync.WaitGroup
for f := range filenames {
wg.Add(1)
go func(f string) {
defer wg.Done()
sizes <- int64(len(f))
}(f)
}
// **closer**, this guy doesn't need to be a goroutine!!
wg.Wait()
close(sizes)
var total int64
for size := range sizes {
total += size
}
result <- total
return total
}
然而,由于 filenames
的长度在运行时是不可知的,所以不可能轻易做到这一点。您必须通读 filenames
,将其存储到一个切片中,然后初始化大小和 for
超过 range filenamesSlice
和...是的,基本上您只是 re-written那时的整个功能。
下面是Go编程书上的示例代码。我不明白为什么 closer 需要成为它自己的 goroutine。我试图将 closer 移动到 main 中,但它崩溃了。有人可以解释为什么 closer 需要在单独的 goroutine 中吗?
谢谢!
func makeThumbnails(filenames <-chan string, result chan<- int64) int64 {
sizes := make(chan int64)
var wg sync.WaitGroup
for f := range filenames {
wg.Add(1)
go func(f string) {
defer wg.Done()
sizes <- int64(len(f))
}(f)
}
// **closer**, why this guy needs to be in a goroutine???
go func() {
wg.Wait()
close(sizes)
}()
var total int64
for size := range sizes {
total += size
}
result <- total
return total
}
问题是 sizes
不是缓冲的 chan
,因此在需要读取 sizes
之前,只有一个匿名 goroutines 可以实际完成。这使得 wg.Wait()
永远等待(因为下一个 goroutine 在 sizes <-
上阻塞并且不能 defer wg.Done()
)和死锁。
通过将 closer 放入单独的 goroutine 中,它可以在准备好时关闭 sizes
chan,并在两者之间从 sizes
开始处理。归根结底,这是 goroutine 的一个很好的用途——启动后忘记关闭!
为了让这段代码在没有 closer goroutine 的情况下工作,你可以简单地将 sizes
初始化为一个缓冲区 >= filenames
.
func makeThumbnails(filenames <-chan string, result chan<- int64) int64 {
sizes := make(chan int64, 10) // buffered channel, now!
// if filenames sends more than 10 strings, though, we're in trouble!!
var wg sync.WaitGroup
for f := range filenames {
wg.Add(1)
go func(f string) {
defer wg.Done()
sizes <- int64(len(f))
}(f)
}
// **closer**, this guy doesn't need to be a goroutine!!
wg.Wait()
close(sizes)
var total int64
for size := range sizes {
total += size
}
result <- total
return total
}
然而,由于 filenames
的长度在运行时是不可知的,所以不可能轻易做到这一点。您必须通读 filenames
,将其存储到一个切片中,然后初始化大小和 for
超过 range filenamesSlice
和...是的,基本上您只是 re-written那时的整个功能。