【问题标题】:Selecting from buffered vs non-buffered channel从缓冲通道和非缓冲通道中进行选择
【发布时间】:2021-10-13 22:55:02
【问题描述】:

当我运行以下简单代码时,它会按预期打印“ping”:

    messages := make(chan string)

    go func() { messages <- "ping" }()
    fmt.Println(<-messages)

但是,当我使用与 select 相同的非缓冲通道时,它不会通过 ping 来完成它,因此会打印“未发送消息”:

    messages := make(chan string)

    select {
    case messages <- "ping":
        fmt.Println("sent message")
    default:
        fmt.Println("no message sent")
    }

为什么会这样?通道是相同的,但是可以通过 goroutine 访问,但不能通过 select 访问。

此外,我发现当我将其转换为缓冲通道(大小为 1)时,它会像魅力一样完成通道:为什么?

    messages := make(chan string,1)

    select {
    case messages <- "ping":
        fmt.Println("sent message")
    default:
        fmt.Println("no message sent")
    }

请注意,没有任何东西挂断,而是立即返回,正如我所描述的那样。

【问题讨论】:

  • 这就是缓冲/非缓冲和默认情况下选择的工作原理;
  • case 1/ chan 没有空间接受该值,所以选择跳转到默认值。案例 2/ 通道有 1 个空间来接受 1 个值,因此选择会跳转到该案例。
  • @mh-cbon 第一个 sn-p 是什么?没有房间但虽然收到的那一个?
  • 你有阅读器,所以它可以工作。不过,该行为与缓冲通道略有不同。下面的答案很好地解释了这一点。你理解它们有困难吗?

标签: go


【解决方案1】:

select 语句阻塞,直到任何一个 cases 准备好,或者如果没有一个准备好,它运行 default 情况。

在您的第二个程序中,select 之前没有出现接收操作,因此 case messages &lt;- "ping" 还没有准备好 - 没有接收器 - 并且始终执行 default

使用缓冲通道,即使另一端没有接收器,发送操作也不会阻塞,因此case messages &lt;- "ping" 已准备好并运行。

在带有 goroutine 的 sn-p 中,发送操作并发运行,因此主程序流程可以继续进行 fmt.Println 调用并阻塞 &lt;-messages,直到并发发送使通道上的值可用

【讨论】:

    【解决方案2】:

    当你使用 Unbuffered 通道类型时,go 需要某种 “检查点”(阻塞语句或其他 goroutine 中的超时) 来切换正在执行的 goroutine 通道将被阻塞,直到有东西读取它,当主 go-routine 进入 select 语句时,选择阻塞,直到有一些活动(消息)或在其中一种情况下发生释放,并且由于您的案例读取尚未完成, 通道阻塞,因为 无缓冲通道需要读取和写入才能充当检查点,因此选择跳转到默认语句。 当您使用缓冲通道时,任何活动都会通过通道发送信号,但在缓冲区满之前不会被阻塞。因此,当使用缓冲通道时,无需阻塞读取器,因此 select 会在通道上检测到要使用的数据。

    也在

    messages := make(chan string)
    go func() { messages <- "ping" }()
    fmt.Println(<-messages)
    

    你在频道上等待

    没有读取通道保持无缓冲通道,阻塞:

    messages := make(chan string)
    select {
        case messages <- "ping":
            fmt.Println("sent message")
        default:
            fmt.Println("no message sent")
        }
    

    缓冲通道不会阻塞,因此将执行写入操作:

    messages := make(chan string,1)
    select {
    case messages <- "ping":
        fmt.Println("sent message")
    default:
        fmt.Println("no message sent")
    }
    

    【讨论】:

      猜你喜欢
      • 2014-01-30
      • 2017-09-06
      • 2019-08-14
      • 2018-04-15
      • 2020-10-23
      • 2019-01-07
      • 2016-08-30
      • 1970-01-01
      • 2016-06-20
      相关资源
      最近更新 更多