为什么在 Golang 中发送大于缓冲通道大小的值会导致死锁错误?

Why does sending a value more than buffered channel size in Golang causes deadlock error?

// By default channels are _unbuffered_, meaning that they
// will only accept sends (`chan <-`) if there is a
// corresponding receive (`<- chan`) ready to receive the
// sent value. _Buffered channels_ accept a limited
// number of  values without a corresponding receiver for
// those values.

package main

import "fmt"

func main() {

    // Here we `make` a channel of strings buffering up to
    // 2 values.
    messages := make(chan string, 2)

    // Because this channel is buffered, we can send these
    // values into the channel without a corresponding
    // concurrent receive.
    messages <- "buffered"
    messages <- "channel"
    messages <- "channel1" //I added this. 

    // Later we can receive these two values as usual.
    fmt.Println(<-messages)
    fmt.Println(<-messages)
}

它抛出的错误:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
    /tmp/sandbox795158698/prog.go:23 +0x8d

问题:

  1. 我们不能发送比缓冲区大小更多的值吗?
  2. 为什么会出现错误 "all goroutines are asleep - deadlock!" 当没有 goroutines 时 在代码中?
  3. 为什么会出现这种僵局?请解释一下?

PlayGroundLink

尝试写入一个完整的通道将被阻塞,直到其他 goroutine 从中读取。在您的程序中,没有其他 goroutine。因此,当您写入完整通道时,主 goroutine 会阻塞,并且由于没有其他 goroutine,因此主 goroutine 不可能继续前进。那就是死锁。

添加到上面的答案:

package main

import (
    "fmt"
    "github.com/practo/klog/v2"
    "os"
    "os/signal"
    "syscall"
    "time"
)

func producer(msgBuf chan<- string) {
    for i := 0; ; i++ {
        fmt.Printf("sent: %d\n", i)
        msgBuf <- fmt.Sprintf("%d", i)
        time.Sleep(1 * time.Second)
    }
}

func process(msgBuf <-chan string) {
    time.Sleep(10 * time.Second)
    for {
        select {
        case msg := <-msgBuf:
            fmt.Printf("processing: %v\n", msg)
            time.Sleep(10 * time.Second)
            fmt.Printf("processed: %v\n", msg)
        }
    }
}

func main() {
    msgBuf := make(chan string, 2)
    go producer(msgBuf)
    go process(msgBuf)

    sigterm := make(chan os.Signal, 1)
    signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM)
    for {
        select {
        default:
        case <-sigterm:
            klog.Info("SIGTERM signal received")
            os.Exit(1)
        }
    }
}

这不会导致死锁,因为您 运行 他们在不同的 go 例程中。