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 關注
- 205 瀏覽
添加回答
舉報