这是在GO中使用频道的正确方法

  • 本文关键字:频道 方法 GO go
  • 更新时间 :
  • 英文 :


在这里,我正在尝试通过res迭代并为每个项目启动一个Goroutine。在每个Goroutine内部,我再次在缓冲频道中启动了3个Goroutines。

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

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
}

将您的close(c)close(ch)移至for ... range循环之前。

一旦一个缓冲,未关闭的通道到达len(ch) == 0for e := range ch { ... }将永远阻止 - 等待另一个Goroutine对通道执行发送语句。闭合表明将不再有发送到频道的元素(任何发送到封闭的通道都会引起恐慌(,并且当通道为空时会导致for e := range ch {...}循环结束。

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

发送到封闭的通道会引起恐慌。您正在收到此错误,因为您的main Goroutine在发送另一个Goroutine之前到达了近距离声明。

您有多种选择如何处理。一种是使用sync.WaitGroup等待所有将发送到通道之前的goroutines,然后再关闭通道。类似:

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 {...},也无需关闭频道。

另一种方法是完全不使用通道。由于您要做的只是打印到Stdout,因此您只需将印刷品移至Goroutines内部,并使用sync.WaitGroup来确保您的main Goroutine直到您的功能Goroutines打印出输出才能退出。

最新更新