为了账号安全,请及时绑定邮箱和手机立即绑定

如何使用 goroutine 执行 for 循环?

如何使用 goroutine 执行 for 循环?

Go
不负相思意 2022-08-24 19:06:02
假设我有一个切片,如:stu = [{"id":"001","name":"A"} {"id":"002", "name":"B"}]也许还有更多这样的元素。切片内部是一个长字符串,我想使用json.unmarshal来解析它。type Student struct {   Id   string `json:"id"`   Name string `json:"name"`}studentList := make([]Student,len(stu))for i, st := range stu {   go func(st string){      studentList[i], err = getList(st)      if err != nil {         return ... //just example      }   }(st)}//and a function like thisfunc getList(stu string)(res Student, error){   var student Student   err := json.Unmarshal(([]byte)(stu), &student)   if err != nil {      return   }   return &student,nil}我得到了nil结果,所以我会说goroutine执行的顺序是无序的,所以我不知道它是否可以使用studentList[i]来获得价值。
查看完整描述

2 回答

?
人到中年有点甜

TA贡献1895条经验 获得超7个赞

以下是代码中的一些潜在问题:


i的值可能不是你所期望的


for i, st := range stu {

   go func(st string){

      studentList[i], err = getList(st)

      if err != nil {

         return ... //just example

      }

   }(st)

}

你启动了一些goroutines,并在其中引用了。问题是,在你启动 goroutine 的时间和 goroutine 引用它的时间之间,它可能已经发生了变化(for 循环同时运行到它启动的 goroutines)。很可能在任何 goroutines 之前的完成意味着所有输出都将存储在 的最后一个元素中(它们将相互覆盖,因此您最终会得到一个值)。iiforstudentList


一个简单的解决方案是传递到 goroutine 函数(例如 (这将创建一个副本)。有关详细信息,请参阅此内容。igo func(st string, i int){}(st, i)


学生列表的输出


你没有在问题中说,但我怀疑你在循环完成后立即运行(或类似)。如上所述,很可能在这一点上没有一个goroutines完成(或者他们可能,你不知道)。使用 a 是一种相当简单的方法:fmt.Println(studentList[1]forWaitGroup


var wg sync.WaitGroup

wg.Add(len(stu))

for i, st := range stu {

    go func(st string, i int) {

        var err error

        studentList[i], err = getList(st)

        if err != nil {

            panic(err)

        }

        wg.Done()

    }(st, i)

}

wg.Wait()

我已经在操场上纠正了这些问题。


查看完整回答
反对 回复 2022-08-24
?
蝴蝶刀刀

TA贡献1801条经验 获得超8个赞

不是因为这个


goroutine执行的顺序不正常


这里至少有两个问题:


您不应该在 goroutine 中使用 for 循环变量。i


多个 goroutines 读取 ,对于循环修改,这里是争用条件。要按预期工作,请将代码更改为:iii


for i, st := range stu {

   go func(i int, st string){

      studentList[i], err = getList(st)

      if err != nil {

         return ... //just example

      }

   }(i, st)

}

更重要的是,用来等待所有的goroutine。sync.WaitGroup


var wg sync.WaitGroup

for i, st := range stu {

   wg.Add(1)

   go func(i int, st string){

      defer wg.Done()


      studentList[i], err = getList(st)

      if err != nil {

         return ... //just example

      }

   }(i, st)

}


wg.Wait()

P.S.:(警告:也许并不总是这样)

这条线,虽然它可能不会引起数据竞争,但它对CPU缓存线并不友好。最好避免编写这样的代码。studentList[i], err = getList(st)


查看完整回答
反对 回复 2022-08-24
  • 2 回答
  • 0 关注
  • 163 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信