在这里,我正在尝试通过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) == 0
,for 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打印出输出才能退出。