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
也许有人会用得上,别像我一样犯傻。 一切都搞清楚了,结果发现 WaitGroup 与此无关。起初我写得完全正确,我需要使用通道进行输出。主函数本身也是一个 goroutine,而 goroutine 之间使用通道进行通信来接收发送的值。
更多关于Golang中main函数嵌套方法内正确使用WaitGroup实现goroutine的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
是的,这个问题在于 Done() 是在 goroutine 中被调用的,而主 goroutine 正在等待它们执行完成,但结果却是空的。然而,如果不使用 goroutine,而是简单地逐个执行,结果就会出现。这意味着我在某个地方没有正确使用 Done()。
你的代码存在几个关键问题: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
}
主要修改点:
- 使用
sync.Mutex保护共享数据的并发访问 - 将配置参数作为闭包参数传入,避免循环变量捕获问题
- 预分配错误切片容量以提高性能
- 方法使用值接收者,避免指针接收者的意外修改
- 在
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
}
通道版本消除了显式的锁操作,通过类型安全的通道传递结果,代码结构更清晰。



