一个带有死锁的Go Channel的简单示例,以及为什么

时间:2019-01-12 08:15:17

标签: go channel goroutine

我的代码如下:

package main

import (
    "fmt"
)

func main() {
    c1 := make(chan int)
    fmt.Println("push c1: ")
    c1 <- 10
    g1 := <- c1
    fmt.Println("get g1: ", g1)
}

当我使用delve调试时,它会显示以下结果:

push c1:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
        D:/Go/projects/hello-world/src/ch9/code9_6/code1.go:10 +0xde
Process 6276 has exited with status 2

我不知道为什么,这只是一个简单的渠道示例,我制作了一个渠道,向它发送价值并从中获取价值,正因为如此,有人可以告诉我原因以及如何纠正它,Thx很多。

2 个答案:

答案 0 :(得分:3)

引用https://tour.golang.org/concurrency/2

  

默认情况下,发送和接收块,直到另一侧准备好为止。   这允许goroutine进行同步而无需显式锁定或   条件变量。

通道只能在goroutine之间工作,但是您只有1个通道。因此c1 <- 10会阻止执行,直到某个人(通常在其他goroutine中)接收到该值。

要解决此问题:

package main

import (
    "fmt"
)

func main() {
    c1 := make(chan int)

    go func() {
        g1 := <- c1 // wait for value
        fmt.Println("get g1: ", g1)
    }()

    fmt.Println("push c1: ")
    c1 <- 10 // send value and wait until it is received.
}

尝试在The Go Playground中执行它。

我建议您从https://tour.golang.org/concurrency/1开始进行正式的Go并发之旅。

编辑:另一个选项是使用缓冲通道,如下所示。但是,使缓冲通道成为并非并不意味着它具有无阻塞的发送/接收操作。这只是意味着它将在队列中的N个值之后阻止发送,其中N始终是预定义的数字。在内部,它将发送的值存储到数组中,并在该数组被填充时阻塞直到接收到值(如非缓冲通道)为止。

package main

import (
    "fmt"
)

func main() {
    c1 := make(chan int, 1) // buffers 1 value without blocking.
    fmt.Println("push c1: ")
    c1 <- 10
    g1 := <- c1
    fmt.Println("get g1: ", g1)
}

Go Playground上尝试。

答案 1 :(得分:0)

通道可以是缓冲的或非缓冲的(这意味着可以向其中发送变量的唯一值),并且当您尝试从非缓冲的通道读取数据时,您将锁定一个goroutine,在其中尝试执行此操作。为了解锁它,您必须在另一个goroutine中写入通道,反之亦然,也就是说,当您在通道中写入内容时,您将锁定执行该操作的goroutine,但是当您尝试从其中读取时,它将被解锁。另一个goroutine中的通道。您应该学习此功能,以开发灵活的(并发)程序。通道用于在必须独立或并行工作的不同例程之间发送数据。您可以创建一个包含两个,三个或更多goroutine的管道,以按顺序将数据从一个goroutine发送到另一个goroutine。

在此示例中,我创建了一个布尔类型为 ch 的通道。确切地说第9行和第11行将在第一行执行是不可能的。 sendq -是正在等待写入通道的列表goroutine。在这种情况下,主行( func main )例程正在等待执行第9行。

enter image description here

我们陷入僵局的另一个例子。执行将永远不会执行到第8行,因此我尝试将数据写入通道的goroutine(下一行)将永远不会启动。

enter image description here

有关渠道的更多信息,您可以在这里找到: http://dmitryvorobev.blogspot.com/2016/08/golang-channels-implementation.html

有m:n调度这样的术语。 Golang有一个m:n调度程序,Go调度程序可以在n个OS线程上调度m个例程。 https://medium.com/@riteeksrivastava/a-complete-journey-with-goroutines-8472630c7f5c

我建议您仔细阅读本书:https://www.amazon.com/Programming-Language-Addison-Wesley-Professional-Computing/dp/0134190440。我认为,这是有关Golang的最好的书,它描述了这种语言的许多概念和功能。

关于并发性和并行性之间的区别,您可以在这里阅读:https://www.ardanlabs.com/blog/2018/12/scheduling-in-go-part3.html