如何解决Go通道死锁?

我正在学习Go编程语言,最近我遇到一个问题,我尝试了很多方法来运行我的代码,但我无法正确运行。我怎样才能改变我的程序来做到这一点?


package main


import (

    "fmt"

    "sync"

)


type Task struct {

    Id       int

    Callback chan int

}


func main() {

    var wg sync.WaitGroup

    subTask := make([]Task, 100)

    for i := 0; i < 100; i++ {

        go func(i int) {

            task := Task{

                Id:       i,

                Callback: make(chan int, 1),

            }

            task.Callback <- i

            subTask = append(subTask, task)

        }(i)

    }


    for _, v := range subTask {

        wg.Add(1)

        go func(v Task) {

            defer wg.Done()

            x := <-v.Callback

            fmt.Printf("%d ", x)

        }(v)

    }

    wg.Wait()

}


繁星点点滴滴
浏览 152回答 4
4回答

慕桂英3389331

正在进行一场数据竞赛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。

肥皂起泡泡

如果通道为零,则<-c来自 c 的接收将永远阻塞。因此出现死锁。通道可能为 nil 的原因是,在执行 goroutine 接收时,第一个 for 循环中的其中一个 goroutine 可能尚未执行。因此,如果您假设第一个 for 循环中的所有 goroutine 在第二个 for 循环开始之前执行,那么您的代码将会正常工作。添加睡眠可以让您看到差异,但您实际上应该解决这个问题。可能出现问题的另一件事是,&nbsp;subTask := make([]Task, 100)此语句在切片中创建 100 个空任务 obj,并追加添加更多内容,因此长度最终会增长到 200。https://play.golang.org/p/4bZDJ2zvKdF

慕侠2389804

您可以考虑一串任务,而不是一部分任务。我认为这保留了您创建 100 个独立读写频道的原始想法。它还避免了数据竞争。func main() {&nbsp; &nbsp; subTasks := make(chan Task)&nbsp; &nbsp; var wg sync.WaitGroup&nbsp; &nbsp; for i := 0; i < 100; i++ {&nbsp; &nbsp; &nbsp; &nbsp; wg.Add(1)&nbsp; &nbsp; &nbsp; &nbsp; go func(i int) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; defer wg.Done()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; task := Task{i, make(chan int)}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; subTasks <- task&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; task.Callback <- i&nbsp; &nbsp; &nbsp; &nbsp; }(i)&nbsp; &nbsp; }&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; wg.Wait()&nbsp; &nbsp; &nbsp; &nbsp; close(subTasks)&nbsp; &nbsp; }()&nbsp; &nbsp; for v := range subTasks {&nbsp; &nbsp; &nbsp; &nbsp; go func(v Task) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(<-v.Callback)&nbsp; &nbsp; &nbsp; &nbsp; }(v)&nbsp; &nbsp; }}在操场上奔跑

HUH函数

一个问题是您要追加到切片而不是更新现有的切片项。而且你不需要缓冲的chan。func main() {&nbsp; &nbsp; subTask := make([]Task, 100)&nbsp; &nbsp; for i := range subTask {&nbsp; &nbsp; &nbsp; &nbsp; go func(i int) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; subTask[i] = Task{i, make(chan int)}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; subTask[i].Callback <- i&nbsp; &nbsp; &nbsp; &nbsp; }(i)&nbsp; &nbsp; }&nbsp; &nbsp; var wg sync.WaitGroup&nbsp; &nbsp; wg.Add(len(subTask))&nbsp; &nbsp; for _, v := range subTask {&nbsp; &nbsp; &nbsp; &nbsp; go func(v Task) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; defer wg.Done()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(<-v.Callback)&nbsp; &nbsp; &nbsp; &nbsp; }(v)&nbsp; &nbsp; }&nbsp; &nbsp; wg.Wait()}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go