在 for 范围内使用 goroutine 运行 for range

我在循环中有一个 goroutine,我想同时为 ms.Entries 中的每个项目执行一个 goroutine,但它只针对循环中的最后一个项目运行。

我从一个更大的程序中抽象出了我的示例...... https://play.golang.org/p/whUMQ3pjq81

package main


import (

    "fmt"

    "sync"

)


type MyStruct struct {

    Entry   *Entry

    Entries *[]Entry

}


type Entry struct {

    ID   int

    Name string

}


type Fn struct {

    Res string

    Err error

}


func main() {

    e1 := Entry{ID: 1, Name: "First"}

    e2 := Entry{ID: 2, Name: "Second"}


    ms := &MyStruct{

        Entries: &[]Entry{e1, e2},

    }


    fmt.Printf("MS: %+v\n", ms)


    var wg sync.WaitGroup

    fnChan := make(chan *Fn)

    go func() {

        wg.Wait()

        close(fnChan)

    }()


    var fns []func() (string, error)

    fns = append(fns, ms.actionA)

    fns = append(fns, ms.actionB)


    for i, entry := range *ms.Entries {

        fmt.Printf("%d: %+v\n", i, entry)

        ms.Entry = &entry

        for j, fn := range fns {

            fmt.Printf("fn loop %d\n", j)

            wg.Add(1)

            go ms.process(&wg, fn, fnChan)

        }

    }


    for d := range fnChan {

        fmt.Printf("fnchan: %+v\n", d)

    }

}


func (m *MyStruct) actionA() (string, error) {

    fmt.Println("actionA")

    fmt.Printf("Entry: %s\n", m.Entry.Name)

    return "actionA done", nil

}


func (m *MyStruct) actionB() (string, error) {

    fmt.Println("actionB")

    fmt.Printf("Entry: %s\n", m.Entry.Name)

    return "actionB done", nil

}


func (m *MyStruct) process(wg *sync.WaitGroup, fn func() (string, error), fnChan chan<- *Fn) {

    fmt.Println("processing")

    var err error

    defer wg.Done()


    res, err := fn()

    if err != nil {

        fnChan <- &Fn{Err: err}

        return

    }


    fnChan <- &Fn{Res: res}

}


MYYA
浏览 153回答 2
2回答

慕虎7371278

你已经陷入了这个陷阱:在循环迭代器变量上使用 goroutine解决这个问题的一种方法是:&nbsp; &nbsp; for j, fn := range fns {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Printf("fn loop %d\n", j)&nbsp; &nbsp; &nbsp; &nbsp; wg.Add(1)&nbsp; &nbsp; &nbsp; &nbsp; // copy the iterator variable to a local variable :&nbsp; &nbsp; &nbsp; &nbsp; // variables declared within the body of a loop are not shared between iterations&nbsp; &nbsp; &nbsp; &nbsp; f := fn&nbsp; &nbsp; &nbsp; &nbsp; go ms.process(&wg, f, fnChan)&nbsp; &nbsp; }

繁星coding

问题你这里有一个问题:ms.Entry = &entry当你使用循环时,像这样:for i, entry := range *ms.Entries {变量“entry”仅声明一次。因此&entry将有一个常数值(每次迭代中的值相同)。但即使您解决了这个问题,另一个问题是您ms在每次迭代中都使用相同的对象。因此,当您启动不同的 goroutine 时,ms.Entry = ...您在第二次迭代中执行的语句将修改共享ms对象。您还fn按照其他答案中的描述在迭代之间“共享”变量,因此您还应该捕获该变量。使固定我建议您从结构中删除该Entry字段(您不需要它,因为您已经拥有完整的数组),并使用数组位置来引用当前条目。i int您应该向“action”和“process”函数添加一个参数。您还需要“捕获”fn变量。for i, entry := range *ms.Entries {&nbsp; &nbsp; ...&nbsp; &nbsp; for j, fn := range fns {&nbsp; &nbsp; &nbsp; &nbsp; ...&nbsp; &nbsp; &nbsp; &nbsp; fn := fn // capture the fn variable&nbsp; &nbsp; &nbsp; &nbsp; go ms.process(&wg, fn, fnChan, i) // sending index here&nbsp; &nbsp; }}请参阅此处修改后的游乐场:https://play.golang.org/p/uuw7r4kGBPb
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go