【问题标题】:All goroutines are asleep - deadlock! ------- Error所有的 goroutine 都在休眠 - 死锁! - - - - 错误
【发布时间】:2015-10-26 17:59:42
【问题描述】:

我想编写三个并发的 go 例程,它们相互发送整数。现在,我的代码已正确编译,但是在第一次执行后它给出了错误“所有 goroutines 都在睡觉 - 死锁!”。我试图找到错误,但我无法在代码逻辑中找到任何错误。任何人都可以帮助我找到我的代码中的错误。我的代码如下。提前致谢。

package main

import "rand"

func Routine1(command12 chan int, response12 chan int, command13 chan int, response13 chan int) {
    for i := 0; i < 10; i++ {
        y := rand.Intn(10)
        if y%2 == 0 {
            command12 <- y
        }

        if y%2 != 0 {
            command13 <- y
        }
        select {
        case cmd1 := <-response12:
            print(cmd1, " 1st\n")
        case cmd2 := <-response13:
            print(cmd2, " 1st\n")
        }
    }
    close(command12)
}

func Routine2(command12 chan int, response12 chan int, command23 chan int, response23 chan int) {
    for i := 0; i < 10; i++ {
        select {
        case x, open := <-command12:
            {
                if !open {
                    return
                }
                print(x, " 2nd\n")
            }

        case x, open := <-response23:
            {
                if !open {
                    return
                }
                print(x, " 2nd\n")
            }
        }

        y := rand.Intn(10)
        if y%2 == 0 {
            response12 <- y
        }

        if y%2 != 0 {
            command23 <- y
        }

    }
}

func Routine3(command13 chan int, response13 chan int, command23 chan int, response23 chan int) {
    for i := 0; i < 10; i++ {
        select {
        case x, open := <-command13:
            {
                if !open {
                    return
                }
                print(x, " 2nd\n")
            }
        case x, open := <-command23:
            {
                if !open {
                    return
                }
                print(x, " 2nd\n")
            }
        }

        y := rand.Intn(10)
        if y%2 == 0 {
            response13 <- y
        }

        if y%2 != 0 {
            response23 <- y
        }

    }
}

func main() {
    command12 := make(chan int)
    response12 := make(chan int)
    command13 := make(chan int)
    response13 := make(chan int)
    command23 := make(chan int)
    response23 := make(chan int)

    go Routine1(command12, response12, command13, response13)
    go Routine2(command12, response12, command23, response23)
    Routine3(command13, response13, command23, response23)
}

谁能告诉我为什么如果我将 Routine2 和 Routine3 声明为 goroutine,为什么输出是 [无输出]。我是 GO 新手,据我从“http://golang.org/doc/effective_go.html#concurrency”中了解到,go 用于与同一地址空间中的其他 goroutine 并行执行 goroutine。那么,问题是什么,所有例程都在运行但输出是[无输出]。

为了使程序更清晰:实际上我很累做的是在每两个例程之间创建两个通道,然后使用一个通道将 int 发送到另一个通道并从该例程通过另一个通道接收 int。例如,例程 1 和 3 之间的通道是命令 13 和响应 13。例程 1 使用 command13 向例程 3 发送 int 和 response13 来接收 int。对于例程 3,response13 用于向例程 1 发送 int,而 command13 用于从例程 1 接收 int(命令/响应 13 代表例程 1 和 3 之间的通道)。现在,由于三个例程是并发的,并且它们有特定的例程来处理接收到的 msg 或发送 msg,为什么它们会死锁?

【问题讨论】:

    标签: go concurrent-programming goroutine


    【解决方案1】:
    go Routine1(command12, response12,command13, response13 )
    go Routine2(command12, response12,command23, response23) // go routine
    Routine3(command12, response12,command23, response23 )
    

    这将在一个新的 goroutine 中启动 Routine1,而主 goroutine 将继续执行下一条语句。因此,Routine1 和 Routine2 将同时执行,但 Routine3 将在 Routine2 完成后启动。您可能会在这里错过另一个“go”语句。

    然后,我试图遵循您的计划。在 Routine1 中,您会这样做

    command13 <- y
    

    这将阻塞 Routine1,直到有另一个 goroutine 准备好能够接收您的消息。所以你需要一个 y := &lt;-command13 在另一个 goroutine 中。

    但是现在,让我们仔细看看另外两个 goroutine 的参数:

    Routine2(command12, response12,command23, response23)
    Routine3(command12, response12,command23, response23 )
    

    如您所见,没有一个 goroutine 可以访问 command13(但您将 command12 传递了两次)。因此,Routine1、Routine2 或 Routine3 都无法继续。死锁!

    我建议你回到绘图板上。先想想你想做什么,画一些关于预期消息流的图表,然后尝试实现那个行为。

    现在调试你的程序真的很难,

    • 我不知道你想做什么。没有关于消息流或类似内容的详细描述。事实上,您的代码根本不包含任何文档。
    • 您正在将名为response23 的通道传递给名为response13 的参数,依此类推。很容易混淆它们。
    • command12 等所有这些通用名称让人很难理解该频道应该做什么
    • 最好在发布源代码之前gofmt :)

    作为起点,我可以向您推荐Go tutorial 中的“质数”示例。在这个例子中,可能的素数从一个 goroutine 传递到另一个。此外,这个示例还包含一些关于消息流的漂亮图形以及一些非常好的解释。你可能会喜欢它。

    【讨论】:

    • 嗨 tux21b,我根据您的建议修改了我的程序,例如添加到例程 3 并更改例程 3(command13, response13,command23, response23)。但是,问题仍然存在。实际上我很累做的是在每两个例程之间创建两个通道,然后在它们之间发送 int 。例如,例程 1 和 3 之间的通道是命令 13 和响应 13。例程 1 使用 command13 发送 int 和 response13 来接收来自例程 3 的 int。[next]
    • 对于例程 3,response13 用于发送 int 和 command13 以从例程 1 接收 int(命令/响应 13 表示例程 1 和 3 之间的通道)。现在,由于三个例程是并发的,并且它们具有处理接收消息或发送消息的特定例程,为什么会死锁?
    • 将第二个 go 语句放在 Routine2 调用之前(而不是 Routine3 调用)。
    【解决方案2】:

    您已将您的频道声明为屏蔽频道。一旦您尝试从其中一个通道进行发送或接收,goroutine 就会阻塞,直到读取该值或接收到一个值。

    例如,在Routine1 中,如果您调用command12 &lt;- y,该goroutine 将阻塞,直到有其他东西将y 拉出通道。 command13 同上。由于您在循环中运行这些发送,并且Routine2Routine3 同步运行,因此您遇到了死锁问题。

    如您所见,通道和 goroutine 都不能保证不会出现死锁。相反,它们通常用于同步和协调并发执行程序的不同部分。

    如果 Routine2Routine3 也是 goroutine,那么没有什么可以阻止您的程序终止——这就是在这种情况下您没有输出的原因。

    坐在一张纸上画出程序中各个元素之间的交互可能会很有帮助。

    【讨论】:

    • 嗨,robothor,你说“如果你调用 command12
    猜你喜欢
    • 1970-01-01
    • 2018-05-28
    • 2014-02-22
    • 1970-01-01
    • 2021-06-30
    • 1970-01-01
    • 2013-11-22
    • 1970-01-01
    相关资源
    最近更新 更多