4 回答
TA贡献2036条经验 获得超8个赞
正在进行一场数据竞赛subTask。subTask任务初始化 goroutine在不同步的情况下读取和写入变量。
该程序的目的是创建并初始化一个包含 100 个Task值的切片,但它创建了一个包含 100 个零值的切片Task,并附加了 100 个以上的初始化Task值(忽略刚才提到的数据竞争问题)。
通过将任务分配给切片元素来修复这两个问题:
for i := 0; i < 100; i++ {
go func(i int) {
task := Task{
Id: i,
Callback: make(chan int, 1),
}
task.Callback <- i
subTask[i] = task
}(i)
}
元素上存在数据竞争subTask。无法保证任务初始化 Goroutines 在主 Goroutine 覆盖这些元素之前完成对元素的写入。通过使用等待组来协调初始化 Goroutine 和主 Goroutine 的完成来修复:
subTask := make([]Task, 100)
for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) {
task := Task{
Id: i,
Callback: make(chan int, 1),
}
task.Callback <- i
subTask[i] = task
wg.Done()
}(i)
}
wg.Wait()
竞争检测器报告上述两种数据竞争。
如果问题中的代码是实际代码,而不是用于提出问题的最小示例,则根本不需要 goroutine。
TA贡献1829条经验 获得超6个赞
如果通道为零,则<-c
来自 c 的接收将永远阻塞。因此出现死锁。通道可能为 nil 的原因是,在执行 goroutine 接收时,第一个 for 循环中的其中一个 goroutine 可能尚未执行。
因此,如果您假设第一个 for 循环中的所有 goroutine 在第二个 for 循环开始之前执行,那么您的代码将会正常工作。
添加睡眠可以让您看到差异,但您实际上应该解决这个问题。
可能出现问题的另一件事是, subTask := make([]Task, 100)
此语句在切片中创建 100 个空任务 obj,并追加添加更多内容,因此长度最终会增长到 200。
https://play.golang.org/p/4bZDJ2zvKdF
TA贡献1719条经验 获得超6个赞
您可以考虑一串任务,而不是一部分任务。
我认为这保留了您创建 100 个独立读写频道的原始想法。它还避免了数据竞争。
func main() {
subTasks := make(chan Task)
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
task := Task{i, make(chan int)}
subTasks <- task
task.Callback <- i
}(i)
}
go func() {
wg.Wait()
close(subTasks)
}()
for v := range subTasks {
go func(v Task) {
fmt.Println(<-v.Callback)
}(v)
}
}
TA贡献1836条经验 获得超4个赞
一个问题是您要追加到切片而不是更新现有的切片项。而且你不需要缓冲的chan。
func main() {
subTask := make([]Task, 100)
for i := range subTask {
go func(i int) {
subTask[i] = Task{i, make(chan int)}
subTask[i].Callback <- i
}(i)
}
var wg sync.WaitGroup
wg.Add(len(subTask))
for _, v := range subTask {
go func(v Task) {
defer wg.Done()
fmt.Println(<-v.Callback)
}(v)
}
wg.Wait()
}
- 4 回答
- 0 关注
- 151 浏览
添加回答
举报