英文:
How can I make a parallel for-loop calling a function that returns 2 parameters?
问题
我想要并行获取数据。例如,我有一个UserID
(字符串)的数组,我想要并行获取数组中的所有用户,并最终得到一个Users
的数组:
func getUser(userIDs []string) {
var users []User
var user User
// 创建一个等待组 - 在N个任务都完成之前阻塞
wg := sync.WaitGroup{}
// 将等待组的计数器加1 - 每个工作线程会将其减1
wg.Add(len(userIDs))
for id := range userIDs {
user, err = h.Repository.GetUserByID(id)
// 这里是问题所在。我应该使用
go h.Repository.GetUserByID(id)
// 以实现并行,但是这样我无法接收到用户的结果
users = append(users, user)
wg.Done()
}
// 现在我们等待所有任务完成 - 再次强调,这不是必须的。
// 你可以从通道中接收N次,然后使用超时或其他安全机制
wg.Wait()
}
我该如何调用这个函数以并行地获取用户,并同时将值保存在数组中?
var user User
应该放在循环内部吗?如果放在循环外部,会有竞态条件吗?
英文:
I want to fetch data in parallels. So for example, I have an array of UserID
(string), and I want to fetch all the users of the array in parallel, and finally have an array of Users
:
func getUser(userIDs []string) {
var users []User
var user User
// We create a waitgroup - basically block until N tasks say they are done
wg := sync.WaitGroup{}
// We add 1 to the wait group - each worker will decrease it back
wg.Add(len(userIDs))
for id := range userIDs {
user, err = h.Repository.GetUserByID(id)
// Here is the problem. I should do a
go h.Repository.GetUserByID(id)
// to be parallel, but then I can not receive the user result
users = append(users, user)
wg.Done()
}
// Now we wait for everyone to finish - again, not a must.
// You can just receive from the channel N times, and use a timeout or something for safety
wg.Wait()
}
How can I call the function that gives me the users in parallel, and at the same time saving the value in the array?
Should the var user User
be inside the loop? Can I have race conditions if it is outside?
答案1
得分: 2
给定你对Repository.GetUserByID
没有控制权,并且没有直接将通道传递给它的方法,我会这样做:
func getUser(userIDs []string) {
var users []User
ch := make(chan User)
for _, id := range userIDs {
go func(ch chan User, id string) {
user, err := h.Repository.GetUserByID(id)
if err != nil {
println(err)
return
}
ch <- user
}(ch, id)
}
for range userIDs {
users = append(users, <-ch)
}
}
从goroutine中写入切片可能会导致并发问题,因此不建议这样做。Golang正好有为此设计的通道。
英文:
Given you have no control over Repository.GetUserByID
, and there is no way to pass the channel directly to it, I would do something like:
func getUser(userIDs []string) {
var users []User
ch := make(chan User)
for id := range userIDs {
go func(ch chan User, id string){
user, err := h.Repository.GetUserByID(id)
if err != nil {
println(err)
return
}
ch <- user
}(ch, id)
}
for range userIDs {
users = append(users, <-ch)
}
}
Writing to a slice from a goroutine might cause concurrency issues. So it is not recommended. Golang has channels exactly for this.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论