【问题标题】:Is this the correct way of using channels in go?这是在go中使用通道的正确方法吗?
【发布时间】:2018-02-27 14:19:16
【问题描述】:

在这里,我尝试迭代 res 并为每个项目启动一个 goroutine。在每个 goroutine 中,我再次在缓冲通道中启动 3 个 goroutine。

运行此代码会阻止完成并且不允许程序完成。

func (aui *AssignmentUtilImpl) MapAssignmentSubmissionData(res []AssignmentSubmissionNode) []AssignmentSubmission {

    if res == nil {
        return nil
    }

    submissions := []AssignmentSubmission{}

    ch := make(chan string, len(res))

    // map data
    for _, val := range res {

        go func(val AssignmentSubmissionNode) {
            sub := AssignmentSubmission{}
            c := make(chan string, 3)

            go mapSubmission(&sub, val, c)
            go mapUser(&sub, val, c)
            go mapFiles(&sub, val, c)

            sub.AssignmentId = val.AssignmentId
            sub.ClassroomId = val.ClassroomId

            for l := range c {
                fmt.Println(l)
            }

            close(c)

            submissions = append(submissions, sub)

            ch <- "submission2: " + sub.Id
        }(val)
    }

    for l := range ch {
        fmt.Println(l)
    }

    close(ch)

    return submissions
}

func mapFiles(sub *AssignmentSubmission, val AssignmentSubmissionNode, c chan string) {
    for _, f := range val.Files {
        file := resourceModule.File{}
        mapstructure.Decode(f.Data, &file)

        sub.Files = append(sub.Files, file)
    }
    c <- fmt.Sprintf("files: %d", len(sub.Files))
}

func mapUser(sub *AssignmentSubmission, val AssignmentSubmissionNode, c chan string) {
    user := userModule.User{}
    mapstructure.Decode(val.User.Data, &user)
    sub.User = user

    c <- "user: " + user.Id
}

func mapSubmission(sub *AssignmentSubmission, val AssignmentSubmissionNode, c chan string) {
    mapstructure.Decode(val.Submission.Data, &sub)

    c <- "submission1: " + sub.Id
}

【问题讨论】:

    标签: go


    【解决方案1】:

    close(c)close(ch) 移到for ... range 循环之前。

    一旦缓冲的、未关闭的通道到达len(ch) == 0for e := range ch { ... } 将永远阻塞——等待另一个 goroutine 执行发送到通道的语句。关闭表示将不再有元素发送到通道(并且任何发送到关闭的通道都会导致恐慌),并且会导致for e := range ch {...} 循环在通道为空时结束。

    它给出以下错误恐慌:在关闭的通道上发送

    发送到关闭的频道会导致恐慌。您收到此错误是因为您的 main goroutine 在发送另一个 goroutine 之前到达了 close 语句。

    对于如何处理这个问题,您有多种选择。一种是使用sync.WaitGroup 等待所有将发送到通道的 goroutine 完成,然后再关闭通道。比如:

    go mapSubmission(&sub, val, c)
    go mapUser(&sub, val, c)
    go mapFiles(&sub, val, c)
    // ...
    wg.Wait()
    close(c)
    for element := range c {
        // ...
    

    另一个是跟踪您期望在通道上发送的次数,删除for e := range ch {...} 循环并将它们替换为将在通道上执行正确次数的接收运算符的循环。在这种情况下,如果您愿意,您还可以使用无缓冲通道而不是缓冲通道。如果知道调用接收操作员的次数,就不需要for e := range ch {...},也不需要关闭频道。

    另一种方法是根本不使用频道。由于您所做的只是打印到标准输出,您可以将打印内容移动到 goroutines 内部,并使用 sync.WaitGroup 确保您的 main goroutine 在您的函数 goroutines 打印其输出之前不会退出。

    【讨论】:

    • 它给出了以下错误panic: send on closed channel
    • 不知何故,通过这种方法使用渠道比不使用渠道花费更多时间。
    • Goroutines 提供并发性。只有某些设计会提供并行性。渠道需要协调,这总是有代价的。但如果速度慢很多,则可能存在错误。
    猜你喜欢
    • 2022-11-22
    • 2015-05-06
    • 2017-09-14
    • 2021-08-07
    • 2012-05-11
    • 2018-12-31
    • 2021-04-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多