你可以使用并行循环来调用返回两个参数的函数。

huangapple go评论82阅读模式
英文:

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 &lt;- user
		}(ch, id)
	}
	for range userIDs {
		users = append(users, &lt;-ch)
	}
}

Writing to a slice from a goroutine might cause concurrency issues. So it is not recommended. Golang has channels exactly for this.

huangapple
  • 本文由 发表于 2021年12月20日 19:18:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/70421005.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定