Golang中main函数嵌套方法内正确使用WaitGroup实现goroutine

Golang中main函数嵌套方法内正确使用WaitGroup实现goroutine 你好,我在主函数中调用了一个方法。在这个方法里,根据结果会启动一些 goroutine 进行处理。我该如何等待它们执行完毕并显示结果呢?

我尝试了下面的方法,但不知为何结果没有显示出来。

func main() {
wgCmd := new(sync.WaitGroup)
var resxhws *sysinfo.HWXEquipData
var xhws = []string{"airportxequip", "appsxequip", "audioxequip", "powerxequip"}
resMoreXHW, resErrsInfo := resxhws.GetMoreHWXInfo(xhws, wgCmd)
wgCmd.Wait()
fmt.Println(resMoreXHW, resErrsInfo)
}

func (morexhw *HWXEquipData) GetMoreHWXInfo(xhws []string, wg *sync.WaitGroup)    (*HWXEquipData, []*ResInfoError) {
resErrs := []*ResInfoError{}

conf, err := config.NewCmd("config/cmd.yml")
if err != nil {
	log.Fatalf("can't read the config: %s", err)
}

var resAir *AirPortData
var resApp *AppsData
var resAudio *AudioData
var resPower *PowerData

for _, xhw := range xhws {
	wg.Add(1)
	if xhw == "airportxequip" {
		go func() {
			defer wg.Done()
			var resErr *ResInfoError
			resAir, resErr = morexhw.GetAirPortXEquip(conf.SysProfileCmd.KeySysProfile, conf.SysProfileCmd.ArgsSysProfile[0])
			resErrs = append(resErrs, resErr)
		}()
	}
	if xhw == "appsxequip" {
		go func() {
			defer wg.Done()
			var resErr *ResInfoError
			resApp, resErr = morexhw.GetAppsXEquip(conf.SysProfileCmd.KeySysProfile, conf.SysProfileCmd.ArgsSysProfile[1])
			resErrs = append(resErrs, resErr)
		}()
	}
	if xhw == "audioxequip" {
		go func() {
			defer wg.Done()
			var resErr *ResInfoError
			resAudio, resErr = morexhw.GetAudioXEquip(conf.SysProfileCmd.KeySysProfile, conf.SysProfileCmd.ArgsSysProfile[2])
			resErrs = append(resErrs, resErr)
		}()
	}
	if xhw == "powerxequip" {
		go func() {
			defer wg.Done()
			var resErr *ResInfoError
			resPower, resErr = morexhw.GetPowerXEquip(conf.SysProfileCmd.KeySysProfile, conf.SysProfileCmd.ArgsSysProfile[10])
			resErrs = append(resErrs, resErr)
		}()
	}
}

morexhw = &HWXEquipData{
	AirPortData: resAir,
	AppsData:    resApp,
	AudioData:   resAudio,
	PowerData:   resPower,
}

return morexhw, resErrs
}

如果我在主函数中直接调用 goroutine,那么一切正常,结果也能显示。但我希望通过接口(方法)来实现这个功能。


更多关于Golang中main函数嵌套方法内正确使用WaitGroup实现goroutine的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

也许有人会用得上,别像我一样犯傻。 一切都搞清楚了,结果发现 WaitGroup 与此无关。起初我写得完全正确,我需要使用通道进行输出。主函数本身也是一个 goroutine,而 goroutine 之间使用通道进行通信来接收发送的值。

更多关于Golang中main函数嵌套方法内正确使用WaitGroup实现goroutine的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


是的,这个问题在于 Done() 是在 goroutine 中被调用的,而主 goroutine 正在等待它们执行完成,但结果却是空的。然而,如果不使用 goroutine,而是简单地逐个执行,结果就会出现。这意味着我在某个地方没有正确使用 Done()

让你的 Goroutine 调用 WaitGroup 上的 Done() 方法。网上有很多关于如何实现这一点的示例。

等待的狗

等待 goroutine 完成

代码示例:sync.WaitGroup 用于等待一组 goroutine 完成。

你的代码存在几个关键问题:1)WaitGroup 在 goroutine 启动前未正确计数;2)并发访问共享切片存在数据竞争;3)方法内对指针接收者的修改不会影响外部变量。以下是修正后的实现:

func main() {
    wgCmd := &sync.WaitGroup{}
    var resxhws sysinfo.HWXEquipData
    
    xhws := []string{"airportxequip", "appsxequip", "audioxequip", "powerxequip"}
    resMoreXHW, resErrsInfo := resxhws.GetMoreHWXInfo(xhws, wgCmd)
    
    wgCmd.Wait()
    fmt.Printf("Results: %+v\nErrors: %+v\n", resMoreXHW, resErrsInfo)
}

func (morexhw HWXEquipData) GetMoreHWXInfo(xhws []string, wg *sync.WaitGroup) (*HWXEquipData, []*ResInfoError) {
    conf, err := config.NewCmd("config/cmd.yml")
    if err != nil {
        log.Fatalf("can't read the config: %s", err)
    }

    var (
        resAir   *AirPortData
        resApp   *AppsData
        resAudio *AudioData
        resPower *PowerData
        mu       sync.Mutex
    )
    
    resErrs := make([]*ResInfoError, 0, len(xhws))
    
    for _, xhw := range xhws {
        wg.Add(1)
        
        switch xhw {
        case "airportxequip":
            go func(key string, arg string) {
                defer wg.Done()
                air, resErr := morexhw.GetAirPortXEquip(key, arg)
                mu.Lock()
                resAir = air
                resErrs = append(resErrs, resErr)
                mu.Unlock()
            }(conf.SysProfileCmd.KeySysProfile, conf.SysProfileCmd.ArgsSysProfile[0])
            
        case "appsxequip":
            go func(key string, arg string) {
                defer wg.Done()
                app, resErr := morexhw.GetAppsXEquip(key, arg)
                mu.Lock()
                resApp = app
                resErrs = append(resErrs, resErr)
                mu.Unlock()
            }(conf.SysProfileCmd.KeySysProfile, conf.SysProfileCmd.ArgsSysProfile[1])
            
        case "audioxequip":
            go func(key string, arg string) {
                defer wg.Done()
                audio, resErr := morexhw.GetAudioXEquip(key, arg)
                mu.Lock()
                resAudio = audio
                resErrs = append(resErrs, resErr)
                mu.Unlock()
            }(conf.SysProfileCmd.KeySysProfile, conf.SysProfileCmd.ArgsSysProfile[2])
            
        case "powerxequip":
            go func(key string, arg string) {
                defer wg.Done()
                power, resErr := morexhw.GetPowerXEquip(key, arg)
                mu.Lock()
                resPower = power
                resErrs = append(resErrs, resErr)
                mu.Unlock()
            }(conf.SysProfileCmd.KeySysProfile, conf.SysProfileCmd.ArgsSysProfile[10])
        }
    }

    result := &HWXEquipData{
        AirPortData: resAir,
        AppsData:    resApp,
        AudioData:   resAudio,
        PowerData:   resPower,
    }
    
    return result, resErrs
}

主要修改点:

  1. 使用 sync.Mutex 保护共享数据的并发访问
  2. 将配置参数作为闭包参数传入,避免循环变量捕获问题
  3. 预分配错误切片容量以提高性能
  4. 方法使用值接收者,避免指针接收者的意外修改
  5. main 中正确调用 Wait() 等待所有 goroutine 完成

如果还需要进一步优化,可以考虑使用通道(channel)来收集结果:

func (morexhw HWXEquipData) GetMoreHWXInfo(xhws []string, wg *sync.WaitGroup) (*HWXEquipData, []*ResInfoError) {
    conf, err := config.NewCmd("config/cmd.yml")
    if err != nil {
        log.Fatalf("can't read the config: %s", err)
    }

    type result struct {
        data interface{}
        err  *ResInfoError
        typ  string
    }
    
    ch := make(chan result, len(xhws))
    defer close(ch)
    
    for _, xhw := range xhws {
        wg.Add(1)
        
        go func(hwType string) {
            defer wg.Done()
            
            var data interface{}
            var resErr *ResInfoError
            
            switch hwType {
            case "airportxequip":
                data, resErr = morexhw.GetAirPortXEquip(
                    conf.SysProfileCmd.KeySysProfile, 
                    conf.SysProfileCmd.ArgsSysProfile[0],
                )
            case "appsxequip":
                data, resErr = morexhw.GetAppsXEquip(
                    conf.SysProfileCmd.KeySysProfile, 
                    conf.SysProfileCmd.ArgsSysProfile[1],
                )
            case "audioxequip":
                data, resErr = morexhw.GetAudioXEquip(
                    conf.SysProfileCmd.KeySysProfile, 
                    conf.SysProfileCmd.ArgsSysProfile[2],
                )
            case "powerxequip":
                data, resErr = morexhw.GetPowerXEquip(
                    conf.SysProfileCmd.KeySysProfile, 
                    conf.SysProfileCmd.ArgsSysProfile[10],
                )
            }
            
            ch <- result{data: data, err: resErr, typ: hwType}
        }(xhw)
    }
    
    wg.Wait()
    
    var (
        resAir   *AirPortData
        resApp   *AppsData
        resAudio *AudioData
        resPower *PowerData
        resErrs  []*ResInfoError
    )
    
    for i := 0; i < len(xhws); i++ {
        r := <-ch
        resErrs = append(resErrs, r.err)
        
        switch r.typ {
        case "airportxequip":
            resAir = r.data.(*AirPortData)
        case "appsxequip":
            resApp = r.data.(*AppsData)
        case "audioxequip":
            resAudio = r.data.(*AudioData)
        case "powerxequip":
            resPower = r.data.(*PowerData)
        }
    }
    
    return &HWXEquipData{
        AirPortData: resAir,
        AppsData:    resApp,
        AudioData:   resAudio,
        PowerData:   resPower,
    }, resErrs
}

通道版本消除了显式的锁操作,通过类型安全的通道传递结果,代码结构更清晰。

回到顶部