【问题标题】:Why does sending a value more than buffered channel size in Golang causes deadlock error?为什么在 Golang 中发送超过缓冲通道大小的值会导致死锁错误?
【发布时间】:2020-08-14 03:44:29
【问题描述】:
// 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

【问题讨论】:

    标签: go channel goroutine


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      添加到上面的答案: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 例程中运行它们。

      【讨论】:

        猜你喜欢
        • 2013-09-10
        • 1970-01-01
        • 1970-01-01
        • 2021-12-25
        • 2015-03-09
        • 2019-12-27
        • 2017-09-06
        • 2015-12-08
        • 2021-08-15
        相关资源
        最近更新 更多