作者:狗狗水灵灵_266 | 来源:互联网 | 2022-12-03 16:22
我目前正在盯着以下代码的增强版本:
func embarrassing(data []string) []string {
resultChan := make(chan string)
var waitGroup sync.WaitGroup
for _, item := range data {
waitGroup.Add(1)
go func(item string) {
defer waitGroup.Done()
resultChan <- doWork(item)
}(item)
}
go func() {
waitGroup.Wait()
close(resultChan)
}()
var results []string
for result := range resultChan {
results = append(results, result)
}
return results
}
这只是在想我的想法.所有这一切都可以用其他语言表达为
results = parallelMap(data, doWork)
即使在Go中不能轻易做到这一点,是不是还有比上面更好的方法呢?
1> icza..:
如果您需要所有结果,则不需要通道(以及关闭它的额外goroutine)来传达结果,您可以直接写入结果切片:
func cleaner(data []string) []string {
results := make([]string, len(data))
wg := &sync.WaitGroup{}
wg.Add(len(data))
for i, item := range data {
go func(i int, item string) {
defer wg.Done()
results[i] = doWork(item)
}(i, item)
}
wg.Wait()
return results
}
这是可能的,因为切片元素充当不同的变量,因此可以单独写入而无需同步.有关详细信息,请参阅可以同时写入不同的切片元素.您也可以按照与输入相同的顺序获得结果.
Anoter变化:如果doWork()
不返回结果但得到结果应该"放置"的地址,另外还有sync.WaitGroup
信号完成,该doWork()
功能可以"直接"作为新的goroutine执行.
我们可以创建一个可重用的包装器doWork()
:
func doWork2(item string, result *string, wg *sync.WaitGroup) {
defer wg.Done()
*result = doWork(item)
}
如果您具有这种格式的处理逻辑,那么它是如何同时执行的:
func cleanest(data []string) []string {
results := make([]string, len(data))
wg := &sync.WaitGroup{}
wg.Add(len(data))
for i, item := range data {
go doWork2(item, &results[i], wg)
}
wg.Wait()
return results
}
另一种变化可以是将信道传递到doWork()
它应该传递结果的信道上.这个解决方案甚至不需要sync.Waitgroup
,因为我们知道要从通道接收多少元素:
func cleanest2(data []string) []string {
ch := make(chan string)
for _, item := range data {
go doWork3(item, ch)
}
results := make([]string, len(data))
for i := range results {
results[i] = <-ch
}
return results
}
func doWork3(item string, res chan<- string) {
res <- "done:" + item
}
最后一个解决方案的"弱点"是它可能收集"无序"结果(这可能是也可能不是问题).通过允许doWork()
接收和返回项目的索引,可以改进此方法以保留顺序.有关详细信息和示例,请参阅如何从按特定顺序执行的N个goroutines中收集值?