一个关于 Go Channel 的死锁及其原因的简单示例

A simple example about Go Channel with deadlock and why

我的代码如下:

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

我不知道为什么,这只是一个简单的通道示例,我创建了一个通道,向它发送值并从中获取值,仅此而已,有人可以告诉我原因以及如何更正它,谢谢很多。

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

By default, sends and receives block until the other side is ready. This allows goroutines to synchronize without explicit locks or condition variables.

通道应该在 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 并发教程

编辑: 另一种选择是使用缓冲通道,如下所示。然而,制作缓冲通道 not 意味着它具有非阻塞 send/receive 操作。这只是意味着它将在队列中的 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 上试用。

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

在这个例子中,我创建了一个 bool 类型的通道 ch。不可能确切地说出第 9 行和第 11 行的哪一行最先执行。 sendq - 是一个等待写入通道的 goroutines 列表。在这种情况下,main (func main) 例程正在等待第 9 行的执行。

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

您可以在此处找到有关频道的更多信息: 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