所有的goroutines都睡着了 - 僵局! -------错误

时间:2011-11-23 16:56:44

标签: go concurrent-programming goroutine

我想写三个并发的例程,它们相互发送整数。现在,我的代码被正确编译,但是在第一次执行之后它会给出错误“所有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声明为常规,为什么输出为[无输出]。我是GO的新手,根据我从“http://golang.org/doc/effective_go.html#concurrency”理解,go用于与同一地址空间中的其他goroutine并行执行goroutine。那么,问题是,所有例程都在运行,但输出是[无输出]。

使程序更清晰:实际上我要做的是在每两个例程之间创建两个通道,然后使用一个通道将int发送到其他通道,并通过该例程中的另一个通道接收int。例如,在例程1和1之间。 3个频道是命令13&amp; response13。例程1使用command13发送int和response13以接收int到/来自例程3.例程3 response13用于发送int和command13以接收int到/来自例程1(命令/响应13表示例程1和3之间的通道)。现在,由于三个例程是并发的,并且它们具有处理收到的消息或发送消息的特定例程,为什么它们会陷入死锁?

2 个答案:

答案 0 :(得分: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完成后启动。你可能会错过另一个“去”声明。

然后,我试图关注你的程序。在Routine1中你做了

command13 <- y

这将阻止Routine1,直到有另一个准备好的goroutine能够接收你的消息。所以你需要另一个goroutine y := <-command13

但是现在,让我们仔细看看其他两个goroutines的参数:

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

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

我建议你回到绘图板。考虑一下你想要做的事情,绘制一些关于预期消息流的图表,然后尝试实现这种行为。

此刻调试程序真的很难,

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

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

答案 1 :(得分:3)

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

例如,在Routine1中,如果您致电command12 <- y goroutine将阻止,直到其他内容从频道中拉出y。同上command13。由于您在循环中运行这些发送,并且Routine2Routine3同步运行,因此您遇到了死锁问题。

你发现,无论是渠道还是goroutines都不能防止僵局。相反,它们通常用于同步和协调同时执行的程序的不同部分。

如果Routine2Routine3也是goroutines,则没有什么可以阻止程序终止 - 这就是为什么在这种情况下你没有输出。

用一张纸坐下来绘制程序中各个元素之间的相互作用可能是有益的。