我在循环中有一个goroutine,我想为ms中的每个项目执行一个goroutine。条目可以同时执行,但仅在循环中的最后一个项目中运行。
我已经从一个更大的程序中提取了我的示例... {{3}}
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}
}
答案 0 :(得分:1)
问题
您在这里遇到问题:
ms.Entry = &entry
使用循环时,如下所示:
for i, entry := range *ms.Entries {
变量“ entry”仅声明一次。
因此&entry
将具有一个恒定值(每次迭代中的值相同)。
但是,即使您解决了该问题,另一个问题是,每次迭代都使用相同的ms
对象。
因此,当您随后启动不同的goroutine时,在第二次迭代中执行的ms.Entry = ...
语句正在修改共享的ms
对象。
您还将在迭代之间“共享” fn
变量,如另一个答案中所述,因此您还应该捕获该变量。
修复
我建议您从结构中删除Entry
字段(因为您已经拥有完整的数组,因此不需要它),并使用数组位置引用当前条目。
您应在“操作”和“过程”功能中添加一个i int
参数。
您还需要“捕获” fn
变量。
for i, entry := range *ms.Entries {
...
for j, fn := range fns {
...
fn := fn // capture the fn variable
go ms.process(&wg, fn, fnChan, i) // sending index here
}
}
在此处查看经过修改的游乐场:
答案 1 :(得分:1)
您已被此陷阱困住:Using goroutines on loop iterator variables
一种解决方法:
for j, fn := range fns {
fmt.Printf("fn loop %d\n", j)
wg.Add(1)
// copy the iterator variable to a local variable :
// variables declared within the body of a loop are not shared between iterations
f := fn
go ms.process(&wg, f, fnChan)
}