3

我想编写三个并发的 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,为什么它们会陷入死锁?

4

2 回答 2

8
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 := <-command13in 另一个 goroutine。

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

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

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

我建议你回到绘图板。首先考虑您要尝试做什么,绘制一些有关预期消息流的图表,然后尝试实现该行为。

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

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

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

于 2011-11-23T18:11:11.657 回答
3

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

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

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

如果Routine2并且Routine3也是 goroutines,那么没有什么可以阻止你的程序终止——这就是为什么在这种情况下你没有得到任何输出。

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

于 2011-11-23T18:14:43.817 回答