有很多方法可以使每次迭代异步执行。其中之一是利用 goroutine 和 channel(如您所愿)。
请看下面的例子。我认为如果我将解释作为 cmets 放在代码的每个部分上会更容易。
// prepare the channel for data transporation purpose between goroutines and main routine
resChan := make(chan []interface{})
for ID, person := range people {
// dispatch an IIFE as goroutine, so no need to change the `someApiCall()`
go func(id string, person Person) {
result := someApiCall(person)
// send both id and result to channel.
// it'll be better if we construct new type based id and result, but in this example I'll use channel with []interface{} type
resChan <- []interface{}{id, result}
}(ID, person)
}
// close the channel since every data is sent.
close(resChan)
// prepare a variable to hold all results
results := make(map[string]Result)
// use `for` and `range` to retrieve data from channel
for res := range ch {
id := res[0].(string)
person := res[1].(Person)
// append it to the map
result[id] = person
}
// And do something with all the results once completed
另一种方法是使用少数sync API,如sync.Mutex 和sync.WaitGroup 来实现相同的目标。
// prepare a variable to hold all results
results := make(map[string]Result)
// prepare a mutex object with purpose is to lock and unlock operations related to `results` variable, to avoid data race.
mtx := new(sync.Mutex)
// prepare a waitgroup object for effortlessly waits for goroutines to finish
wg := new(sync.WaitGroup)
// tell the waitgroup object how many goroutines that need to be finished
wg.Add(people)
for ID, person := range people {
// dispatch an IIFE as goroutine, so no need to change the `someApiCall()`
go func(id string, person Person) {
result := someApiCall(person)
// lock the append operation on `results` variable to avoid data race
mtx.Lock()
results[ID] = result
mtx.Unlock()
// tell waitgroup object that one goroutine is just finished
wg.Done()
}(ID, person)
}
// block the process synchronously till all goroutine finishes.
// after that it'll continue to next process underneath
wg.Wait()
// And do something with all the results once completed
警告。上述两种方法都适用于需要迭代的数据很少的情况。多的话就不好了,几乎同时调度成吨的goroutine,会造成非常高的机器内存占用。我建议看看worker pool technique 来改进代码。