Голанг Чан зависает при просмотре результатов

Я новичок в каналах GoLang, но он работает не так, как я ожидал.

У меня есть функция, для которой я хочу вызвать 3 отдельные горутины, а затем дождаться их завершения. Если я получаю сообщение об ошибке, я пытаюсь поместить его в CHAN, а затем обработать ошибку после завершения wg.Wait().

К сожалению, при просмотре результатов CHAN он зависает. Я предполагаю, что он все еще ждет заполнения CHAN, но не все горутины будут вызывать ошибки.

Как лучше всего справиться с зацикливанием каналов, которые не всегда заполняются? Я также знаю, что здесь мне необязательно использовать 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
}

person mornindew    schedule 10.10.2018    source источник
comment
Проблема в том, что канал может быть пустым, и работа диапазона блокируется до тех пор, пока не будет прочитано что-то из канала. Вы можете просто закрыть канал после wg.Wait (). Потому что вы можете быть уверены, что в него больше не будет записывать рутину.   -  person Feralus    schedule 10.10.2018
comment
Спасибо. Это почти именно то, чем я закончил, и это сработало. Спасибо за помощь.   -  person mornindew    schedule 10.10.2018


Ответы (1)


Итак, вскоре после публикации я придумал решение. Моя проблема была действительно двоякой:

  1. Закрыл свой канал, чтобы он знал, когда перестать слушать
  2. Использование несовместимых методов для передачи данных из моей горутины в вызывающую функцию. Для ошибок я использовал 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"}
    }

    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
}
person mornindew    schedule 10.10.2018