douqian8238 2019-08-26 18:00
浏览 43
已采纳

用于并行获取多个字段的模式

I need to fetch multiple fields in parallel for my system from external services (in this example, simulated by Name(), Age() and CanDrive() methods).

The fetchUser() method does what I want, but it seems too verbose specially if you consider I could have 10+ fields. Are there better ways I can implement this?

playground: https://play.golang.org/p/90sNq1GmrD8

Code (same as in playground):

package main

import (
    "fmt"
    "sync"
)

type User struct {
    Name string
    Age int
    CanDrive *bool
}

func Name() (string, error) {
    return "foobar", nil
}

func Age() (int, error) {
    return 25, nil
}

func CanDrive() (bool, error) {
    return true, nil
}

func fetchUser() (*User, error) {
    var wg sync.WaitGroup
    errs := make(chan error)

    user := &User{}

    wg.Add(1)
    go func() {
        var err error
        defer wg.Done()
        user.Name, err = Name()
        errs <- err
    }()

    wg.Add(1)

    go func() {
        var err error
        defer wg.Done()
        user.Age, err = Age()
        errs <- err
    }()

    wg.Add(1)
    go func() {
        defer wg.Done()
        canDrive, err := CanDrive()
        if err == nil {
            user.CanDrive = &canDrive
        }
        errs <- err
    }()

    // wait until all go-routines are completed successfully
    // if that's the case, close the errs channel
    go func() {
        wg.Wait()
        close(errs)
    }()

    // keep waiting for errors (or for the error channel to be closed
    // if all calls succeed)
    for err := range errs {
        if err != nil {
            return nil, err
        }
    }

    return user, nil
}

func main() {
    user, _ := fetchUser()
    fmt.Println(user)
}
  • 写回答

1条回答 默认 最新

  • doujie7497 2019-08-26 18:13
    关注

    Without knowing more of the specifics of your scenario, my only suggestion would be to separate out the Go routine error handling into another package.

    Fortunately, a package already exists that does the same thing, named errgroup. Below is an implementation of your original code using the errgroup package:

    package main
    
    import (
        "context"
        "fmt"
    
        "golang.org/x/sync/errgroup"
    )
    
    type User struct {
        Name     string
        Age      int
        CanDrive *bool
    }
    
    func Name() (string, error) {
        return "foobar", nil
    }
    
    func Age() (int, error) {
        return 25, nil
    }
    
    func CanDrive() (bool, error) {
        return true, nil
    }
    
    func fetchUser(ctx context.Context) (*User, error) {
        group, ctx := errgroup.WithContext(ctx)
    
        user := &User{}
        group.Go(func() (err error) {
            user.Name, err = Name()
            return
        })
        group.Go(func() (err error) {
            user.Age, err = Age()
            return
        })
        group.Go(func() error {
            canDrive, err := CanDrive()
            if err == nil {
                user.CanDrive = &canDrive
            }
            return err
        })
    
        if err := group.Wait(); err != nil {
            return nil, err
        }
    
        return user, nil
    }
    
    func main() {
        user, err := fetchUser(context.Background())
        fmt.Println(user, err)
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 关于#MATLAB#的问题,如何解决?(相关搜索:信噪比,系统容量)
  • ¥500 52810做蓝牙接受端
  • ¥15 基于PLC的三轴机械手程序
  • ¥15 多址通信方式的抗噪声性能和系统容量对比
  • ¥15 winform的chart曲线生成时有凸起
  • ¥15 msix packaging tool打包问题
  • ¥15 finalshell节点的搭建代码和那个端口代码教程
  • ¥15 Centos / PETSc / PETGEM
  • ¥15 centos7.9 IPv6端口telnet和端口监控问题
  • ¥20 完全没有学习过GAN,看了CSDN的一篇文章,里面有代码但是完全不知道如何操作