Goroutines 并发、独立地运行,因此如果没有显式同步,您无法预测执行和完成顺序。因此,您不能将返回的数字与输入的数字配对。
您可以返回更多数据(例如,输入数字和输出,例如包装在结构中),或者将指针传递给工作函数(作为新的 goroutines 启动),例如*Stuff 并让 goroutine 在 Stuff 本身中填充计算数据。
返回更多数据
我将使用chan Pair 的频道类型,其中Pair 是:
type Pair struct{ Number, Result int }
所以计算将如下所示:
func doubleNumber(i int, c chan Pair) { c <- Pair{i, i + i} }
func powerNumber(i int, c chan Pair) { c <- Pair{i, i * i} }
我将使用map[int]*Stuff,因为可收集的数据来自多个渠道(double 和power),我想轻松快速地找到合适的Stuff(需要指针,所以我也可以修改它“在地图中”)。
所以主函数:
nums := []int{2, 3, 4} // given numbers
stuffs := map[int]*Stuff{}
double := make(chan Pair)
power := make(chan Pair)
for _, i := range nums {
go doubleNumber(i, double)
go powerNumber(i, power)
}
// How do I get the values back in the right order?
for i := 0; i < len(nums)*2; i++ {
getStuff := func(number int) *Stuff {
s := stuffs[number]
if s == nil {
s = &Stuff{Number: number}
stuffs[number] = s
}
return s
}
select {
case p := <-double:
getStuff(p.Number).Double = p.Result
case p := <-power:
getStuff(p.Number).Power = p.Result
}
}
for _, v := range nums {
fmt.Printf("%+v\n", stuffs[v])
}
输出(在Go Playground 上试试):
&{Number:2 Double:4 Power:4}
&{Number:3 Double:6 Power:9}
&{Number:4 Double:8 Power:16}
使用指针
由于现在我们正在传递*Stuff 值,我们可以在Stuff 本身中“预填充”输入数字。
但必须小心,您只能通过适当的同步读取/写入值。最简单的方法是等待所有“worker” goroutine 完成它们的工作。
var wg = &sync.WaitGroup{}
func main() {
nums := []int{2, 3, 4} // given numbers
stuffs := make([]Stuff, len(nums))
for i, n := range nums {
stuffs[i].Number = n
wg.Add(2)
go doubleNumber(&stuffs[i])
go powerNumber(&stuffs[i])
}
wg.Wait()
fmt.Printf("%+v", stuffs)
}
func doubleNumber(s *Stuff) {
defer wg.Done()
s.Double = s.Number + s.Number
}
func powerNumber(s *Stuff) {
defer wg.Done()
s.Power = s.Number * s.Number
}
输出(在Go Playground上试试):
[{Number:2 Double:4 Power:4} {Number:3 Double:6 Power:9} {Number:4 Double:8 Power:16}]
同时写入不同的切片元素
另外请注意,由于您可以同时写入不同的数组或切片元素(详情请参阅Can I concurrently write different slice elements),您可以将结果直接写入没有通道的切片中。请参阅 Refactor code to use a single channel in an idiomatic way 如何做到这一点。