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

时间:2020-04-29 21:41:37

标签: go channel goroutine

// 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. 为什么错误显示 “所有goroutine都在睡着-死锁!”没有goroutines时 在代码中?
  3. 为什么这是一个僵局?请解释吗?

PlayGroundLink

2 个答案:

答案 0 :(得分:3)

写入完整通道的尝试将被阻止,直到从其读取其他goroutine为止。在您的程序中,没有其他goroutines。因此,当您写入完整通道时,主goroutine会阻塞,并且由于没有其他goroutine,因此主goroutine永远不会前进。那是一个僵局。

答案 1 :(得分:0)

添加到上面的答案:https://stackoverflow.com/a/61512364/4106031

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 例程中运行它们。