英文:
How to exit a channel range / collect results
问题
我需要同时处理几个任务,然后“收集”结果。以下是我想出的代码,但我想知道这是否是正确的方法(即惯用法/最佳实践),或者是否有我可能忽略的问题。
package main
import "fmt"
import "sync"
func main() {
// ch 是 int 类型的提供者。容量是 99,但实际上应该是 3
ch := make(chan int, 99)
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
ch <- i
}(i)
}
wg.Wait()
for v := range ch {
fmt.Println("consume ", v)
if len(ch) == 0 {
close(ch)
}
}
fmt.Printf("All done")
}
请注意,这段代码使用了 goroutine 和 channel 来实现并发任务处理和结果收集。首先,它创建了一个带有 99 个缓冲区的 int 类型的 channel。然后,使用 sync.WaitGroup 来跟踪 goroutine 的完成情况。在循环中,它启动了 3 个 goroutine,每个 goroutine 将一个值发送到 channel 中。然后,使用 range 循环从 channel 中接收值,并打印出来。最后,当 channel 中没有值时,关闭 channel,并打印出 "All done"。这段代码的实现方式是合理的,并且符合 Go 语言的惯用法和最佳实践。
英文:
I need to process several tasks concurrently and then "collect" the results. Below is the code I came up with but I'm wondering if it's the right way to do this(i.e idiomatic/ best practice) or if there is something wrong that I might miss.
package main
import "fmt"
import "sync"
func main() {
// ch is the int provider. Cap is 99 but it should
// really be 3
ch := make(chan int, 99)
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
ch <- i
}(i)
}
wg.Wait()
for v := range ch {
fmt.Println("consume ", v)
if len(ch) == 0 {
close(ch)
}
}
fmt.Printf("All done")
}
答案1
得分: 1
这是要翻译的内容:
那样做没有问题,它可以工作。然而,关闭通道的工作应该由生产者来完成,而不是消费者。
为此,我建议将整个生产者过程移入一个 goroutine,并让它等待,然后关闭通道:
package main
import "fmt"
import "sync"
func main() {
ch := make(chan int, 3)
var wg sync.WaitGroup
go func() {
for i := 0; i < 3; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
ch <- i
}(i)
}
wg.Wait()
close(ch) // 生产者现在关闭通道
}()
// 消费者的唯一责任是迭代通道
for v := range ch {
fmt.Println("consume ", v)
}
fmt.Printf("All done")
}
[在 Go Playground 上查看](http://play.golang.org/p/wT_YjPMtEF)
英文:
There's nothing wrong with that .. it works. However, it really should be the producers job to close the channel (not the consumer).
To that end .. I would propose that you move the entire producer process into a goroutine and have that wait .. then close the channel:
package main
import "fmt"
import "sync"
func main() {
ch := make(chan int, 3)
var wg sync.WaitGroup
go func() {
for i := 0; i < 3; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
ch <- i
}(i)
}
wg.Wait()
close(ch) // producer is now closing the channel
}()
// Consumer has the single responsibility of iterating over the channel
for v := range ch {
fmt.Println("consume ", v)
}
fmt.Printf("All done")
}
<kbd>See it in the Go Playground</kbd>
答案2
得分: 1
我确定你的例子是人为构造的,但是你的例子可以简化得多:
ch := make(chan int, 99)
for i := 0; i < 3; i++ {
go func(i int) {
ch <- i
}(i)
}
for i := 0; i < 3; i++ {
v := <- ch
fmt.Println("consume ", v)
}
fmt.Println("All done")
你的真实代码可能更复杂,可能需要类似于 waitGroup 的东西,如果是这种情况,请尽力解释你的具体问题。
在范围内关闭通道以退出范围似乎不符合 Go 的惯用法。
英文:
I am sure your example is contrived, but your example could be made much simpler:
ch := make(chan int, 99)
for i := 0; i < 3; i++ {
go func(i int) {
ch <- i
}(i)
}
for i := 0; i < 3; i++ {
v := <- ch
fmt.Println("consume ", v)
}
fmt.Println("All done")
Your real code may be more complex and require something like a waitGroup, and if that is the case please do whatever you can to explain your specific problem.
Closing a channel within a range in order to exit the range seems like non-idiomatic Go.
<kbd>Go Play</kbd>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论