【问题标题】:Go lang multiplexing all goroutines are asleep - deadlockGo lang 多路复用所有 goroutine 都处于休眠状态 - 死锁
【发布时间】:2018-04-30 18:58:02
【问题描述】:

我想使用多个 go 例程返回通道创建一个扇入函数,这是我的代码。

package main

import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)

var wg, wg2 sync.WaitGroup

func main() {
    final := talk(boring("Joe"), boring("Ann"))
    for i := 0; i < 10; i++ {
        fmt.Println(<-final)
    }
    fmt.Println("You are both boring I'm leaving")
}

func talk(input1, input2 <-chan string) <-chan string {
    out := make(chan string)
    go func() {
        wg.Add(1)
        for {
            out <- <-input1
        }
    }()
    go func() {
        wg.Add(1)
        for {
            out <- <-input2
        }
    }()
    wg.Done()
    close(out)
    return out
}

func boring(msg string) <-chan string {
    c := make(chan string)
    for i := 0; i < 5; i++ {
        c <- fmt.Sprintf("%s%d\n", msg, i)
        time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
    }
    return c
}

但是运行上面的代码后出现错误

所有 goroutine 都处于休眠状态 - 死锁

我已尝试关闭频道,但仍然出现错误。我试图将无聊的返回频道分配给乔和安,然后将这些频道传递给多路复用的通话功能,但仍然没有成功。我是新来的,学习渠道对这个概念不太清楚。

【问题讨论】:

    标签: go


    【解决方案1】:

    你可以使用select:https://tour.golang.org/concurrency/5代替等待组

    select 语句让 goroutine 等待多个通信 操作。

    A select 阻塞,直到它的一个案例可以运行,然后它执行那个 案子。如果多个都准备好了,它会随机选择一个。

    package main
    
    import (
        "fmt"
        "math/rand"
        "time"
    )
    
    func main() {
        final := talk(boring("Joe"), boring("Ann"))
        for i := 0; i < 10; i++ {
            fmt.Println(<-final)
        }
        fmt.Println("You are both boring I'm leaving")
    }
    
    func talk(input1, input2 <-chan string) <-chan string {
        c := make(chan string)
        go func() {
            for {
                select {
                case s := <-input1:
                    c <- s
                case s := <-input2:
                    c <- s
                }
            }
        }()
        return c
    }
    
    func boring(msg string) <-chan string {
        c := make(chan string)
        go func() {
            for i := 0; i < 5; i++ {
                c <- fmt.Sprintf("%s: %d", msg, i)
                time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
            }
        }()
        return c
    }
    

    Try it on Playground

    编辑:

    在您给定的示例中,boring 函数不使用 goroutine 通过通道重复发送,这将永远阻塞,因为: https://tour.golang.org/concurrency/2

    默认情况下,发送和接收块,直到对方准备好。 这允许 goroutines 在没有显式锁或 条件变量。

    另外,wg.Done() 需要成为 goroutine 的一部分。

    我通过进行上述更改使其工作:https://play.golang.org/p/YN0kfBO6iT

    【讨论】:

    • 你说得对,我可以选择 Select 而不是等待不知道哪个在管道中的例程。但我想知道它造成死锁的确切原因。我主要担心的是我的代码无法正常工作的原因。
    • 是的,我也做过。因为我再次检查我的代码并发现我没有使用 goroutine 将值发送到侧面无聊函数中的通道。感谢更新
    【解决方案2】:

    你不能自己停止一个 goroutine,我建议用类似的方式来通知 goroutine 退出:

    stop := make(chan bool)
    go func() {
        for {
            select {
            case <- stop:
                return
            default:
                // Do other stuff
            }
        }
    }()
    
    // Do stuff
    
    // Quit goroutine
    stop<- true
    

    【讨论】:

    • 我试过了。但它不起作用。我没有得到哪个 goroutine 造成了死锁,为什么?
    • 在运行中,非缓冲通道将阻塞,直到通道的另一端发生某些事情,无论您是发送还是接收。因此,对于您的情况,请缓冲您的频道,这应该会有所帮助。 tour.golang.org/concurrency/3
    猜你喜欢
    • 2013-11-22
    • 2012-09-06
    • 2021-04-05
    • 2015-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-02
    • 1970-01-01
    相关资源
    最近更新 更多