Go 这是围棋中使用通道的正确方法吗?

Go 这是围棋中使用通道的正确方法吗?,go,Go,在这里,我试图迭代res并为每个项目启动一个goroutine。在每个goroutine中,我再次在一个缓冲通道中启动3个goroutine 运行此代码会阻止完成,不允许程序完成 func (aui *AssignmentUtilImpl) MapAssignmentSubmissionData(res []AssignmentSubmissionNode) []AssignmentSubmission { if res == nil { return nil

在这里,我试图迭代
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
}
func(aui*AssignmentUtimpl)MapAssignmentSubmissionData(res[]AssignmentSubmissionNode)[]AssignmentSubmissionSubmission{
如果res==nil{
归零
}
提交:=[]分配提交{}
ch:=制造(成串,透镜(分辨率))
//地图数据
对于u,val:=范围分辨率{
go func(val分配提交节点){
sub:=AssignmentSubmission{}
c:=制造(成串,3)
转到地图提交(&sub,val,c)
转到地图用户(&sub,val,c)
转到映射文件(&sub、val、c)
sub.AssignmentId=val.AssignmentId
sub.ClassroomId=val.ClassroomId
对于l:=范围c{
fmt.Println(左)
}
关闭(c)
提交=附加(提交,子)
ch将
close(c)
close(ch)
移动到
for…range
循环之前

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

它给出以下错误提示:在封闭通道上发送

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

对于如何处理此问题,您有多个选项。其中一个选项用于在关闭频道之前等待将发送到频道的所有goroutine完成。类似于:

go mapSubmission(&sub, val, c)
go mapUser(&sub, val, c)
go mapFiles(&sub, val, c)
// ...
wg.Wait()
close(c)
for element := range c {
    // ...
另一种方法是跟踪通道上预期的发送数,删除e:=range ch{…}的
循环并将其替换为一个循环,该循环将在通道上执行正确次数的接收运算符。在这种情况下,如果您愿意,您也可以使用无缓冲通道而不是缓冲通道。如果您知道调用接收运算符的次数,您不需要e:=范围ch{…}的
,无需关闭频道


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

它给出以下错误
panic:在封闭通道上发送
以某种方式使用通道比不使用通道花费更多的时间。Goroutines提供并发性。只有某些设计会提供并行性。通道需要协调,这总是有成本的。但如果速度慢得多,则可能会出现错误。