Golang Chan在遍历结果时挂了

时间:2018-10-10 05:30:23

标签: asynchronous go goroutine

我对GoLang频道还很陌生,但效果不如预期。

我有一个函数,我想调用3个独立的goroutine,然后等待它们全部完成。如果出现错误,则尝试将其放在CHAN上,然后在wg.Wait()完成后处理该错误。

不幸的是,当循环遍历CHAN结果时,它将挂起。我推测是因为它仍在等待CHAN填充,但并非所有goroutine都会抛出错误。

处理不总是填充的通道的最佳方法是什么?我也知道我不必在这里使用CHAN,但是我想确保我理解它们。

下面是我的代码。

func createWorkoutPlanForUserPreconditionCheck(planID, userID, transactionID *string) (*sharedstructs.Plan, *sharedstructs.User, *sharedstructs.Profile, error) {
    if planID == nil || userID == nil || transactionID == nil {
        return nil, nil, nil, sharedstructs.InvalidData{Msg: "Cannot pass in Nil Parameters"}
    }

    plan := sharedstructs.Plan{}
    user := sharedstructs.User{}
    profile := sharedstructs.Profile{}
    //myError := sharedstructs.InvalidData{}
    ch := make(chan sharedstructs.InvalidData, 3)

    var wg sync.WaitGroup
    wg.Add(3)
    //Get the Plan from the Plan ID
    go func() {
        defer wg.Done()
        returnedPlan, readError := readPlan(*planID)
        if readError != nil || returnedPlan == nil {
            ch <- sharedstructs.InvalidData{Msg: "Could Not Retreive the User with ID: " + *userID}
        } else {
            plan = *returnedPlan
        }
    }()

    //Get the User
    go func() {
        defer wg.Done()
        returnedUser, getUserError := userdomain.GetUserByID(*userID, *transactionID)
        if getUserError != nil || &returnedUser == nil {
            ch <- sharedstructs.InvalidData{Msg: "Could Not Retreive the User with ID: " + *userID}
        } else {
            user = returnedUser
        }
    }()

    //Get the Profile
    go func() {
        defer wg.Done()
        readProfile, getProfileError := profiledomain.GetProfile(*userID, *transactionID)
        if getProfileError != nil || readProfile == nil {
            ch <- sharedstructs.InvalidData{Msg: "Could Not Retreive the User with ID: " + *userID}
        } else {
            profile = *readProfile
        }
    }()

    wg.Wait()

    ////"Hangs Here" - PUT MY ERROR HANDLING LOGIC HERE
    for err := range ch {
        fmt.Println(err.Error())
    }

    return &plan, &user, &profile, nil
}

1 个答案:

答案 0 :(得分:1)

因此,发布后不久我就找到了解决方案。我的问题实际上有两个方面:

  1. 关闭频道,使其知道何时停止收听
  2. 使用不一致的方法将我的数据从goroutine获取到调用函数。对于错误,我使用chan,但是对于自定义结构,我只是对其进行设置。我将chan泛化为interface {},然后在进行处理以确定其结构类型时对其进行了类型切换。

^^^解决这些问题使我的代码正常工作,但这就是我的代码最终的结果。

func createWorkoutPlanForUserPreconditionCheck(planID, userID, transactionID *string) (*sharedstructs.Plan, *sharedstructs.User, *sharedstructs.Profile, error) {
    if planID == nil || userID == nil || transactionID == nil {
        return nil, nil, nil, sharedstructs.InvalidData{Msg: "Cannot pass in Nil Parameters"}
    }

    outputChannel := make(chan interface{}, 3)
    var wg sync.WaitGroup
    wg.Add(3)

    //Get the Plan from the Plan ID
    go func() {
        defer wg.Done()
        returnedPlan, readError := readPlan(*planID)
        if readError != nil || returnedPlan == nil {
            outputChannel <- sharedstructs.InvalidData{Msg: "Could Not Retreive the User with ID: " + *userID}
        } else {
            outputChannel <- *returnedPlan
        }
    }()

    //Get the User
    go func() {
        defer wg.Done()
        returnedUser, getUserError := userdomain.GetUserByID(*userID, *transactionID)
        if getUserError != nil || &returnedUser == nil {
            outputChannel <- sharedstructs.InvalidData{Msg: "Could Not Retreive the User with ID: " + *userID}
        } else {
            outputChannel <- returnedUser
        }
    }()

    //Get the Profile
    go func() {
        defer wg.Done()
        readProfile, getProfileError := profiledomain.GetProfile(*userID, *transactionID)
        if getProfileError != nil || readProfile == nil {
            outputChannel <- sharedstructs.InvalidData{Msg: "Could Not Retreive the User with ID: " + *userID}
        } else {
            outputChannel <- *readProfile
        }
    }()

    wg.Wait()
    close(outputChannel)

    plan := sharedstructs.Plan{}
    user := sharedstructs.User{}
    profile := sharedstructs.Profile{}
    for result := range outputChannel {
        switch result.(type) {
        case sharedstructs.InvalidData:
            return nil, nil, nil, result.(sharedstructs.InvalidData)
        case sharedstructs.Plan:
            plan = result.(sharedstructs.Plan)
        case sharedstructs.User:
            user = result.(sharedstructs.User)
        case sharedstructs.Profile:
            profile = result.(sharedstructs.Profile)
        }
    }

    return &plan, &user, &profile, nil
}