douyao4632 2019-07-17 15:59
浏览 70
已采纳

使用for范围内的goroutine运行范围

I have a goroutine inside a loop and I want to execute a goroutine for each item in ms.Entries concurrently but it's only running for the last item in the loop.

I've abstracted my example out of a larger program... 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
", 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
", i, entry)
        ms.Entry = &entry
        for j, fn := range fns {
            fmt.Printf("fn loop %d
", j)
            wg.Add(1)
            go ms.process(&wg, fn, fnChan)
        }
    }

    for d := range fnChan {
        fmt.Printf("fnchan: %+v
", d)
    }
}

func (m *MyStruct) actionA() (string, error) {
    fmt.Println("actionA")
    fmt.Printf("Entry: %s
", m.Entry.Name)
    return "actionA done", nil
}

func (m *MyStruct) actionB() (string, error) {
    fmt.Println("actionB")
    fmt.Printf("Entry: %s
", 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}
}
  • 写回答

2条回答 默认 最新

  • dongqiao3214 2019-07-17 16:06
    关注

    Problem

    You have a problem here:

    ms.Entry = &entry
    

    When you use a loop, like this:

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

    The variable "entry" is only declared once.

    So &entry will have a constant value (same value in every iteration).

    But even if you solve that issue, another problem is that you are using the same ms object on every iteration.

    So when you then launch different goroutines, the ms.Entry = ... statement you execute on the second iteration is modifying the shared ms object.

    You are also "sharing" the fn variable between iterations as described in the other answer, so you should also capture that variable.

    Fix

    I suggest you remove the Entry field from your struct (you don't need it since you already have the complete array), and you use the array position to refer to the current entry.

    You should add an i int parameter to your "action" and "process" functions.

    You also need to "capture" the fn variable.

    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
        }
    }
    

    See here for a modified playground:

    https://play.golang.org/p/uuw7r4kGBPb

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥60 ESP32怎么烧录自启动程序
  • ¥50 html2canvas超出滚动条不显示
  • ¥15 java业务性能问题求解(sql,业务设计相关)
  • ¥15 52810 尾椎c三个a 写蓝牙地址
  • ¥15 elmos524.33 eeprom的读写问题
  • ¥15 使用Java milo连接Kepserver服务端报错?
  • ¥15 用ADS设计一款的射频功率放大器
  • ¥15 怎么求交点连线的理论解?
  • ¥20 软件开发方法学习来了
  • ¥15 微信小程序商城如何实现多商户收款 平台分润抽成