如何理解golang内存模型中的channel通信规则?

How to understand the channel communication rules in golang memory model?

学习golang途中,在尝试理解内存模型规范中描述的通道通信时有点困惑,如下所示:

  1. A send on a channel happens before the corresponding receive from that channel completes.
  2. The closing of a channel happens before a receive that returns a zero value because the channel is closed.
  3. A receive from an unbuffered channel happens before the send on that channel completes.
  4. The kth receive on a channel with capacity C happens before the k+Cth send from that channel completes.

前 2 条规则清晰易懂,而我真的对第三条规则感到困惑,这似乎与其他规则相反...我是否错过了关于 无缓冲通道的任何特别之处?或者我是正确的如果我按照规范中的示例如下所示:

var c = make(chan int)
var a string

func f() {
    a = "hello, world"
    <-c    // A
}
func main() {
    go f()
    c <- 0 // B
    print(a)
}

对于无缓冲通道,发送操作 (B) 会被阻塞,直到接收方准备好接收值 (A)? (比如:B 开始但不 return 直到 A 执行)它准确吗?

而且我在Effective Go spec中找到了以下说法,但我的理解仍然存在差异......所以谁能简单明了地解释一下?

Receivers always block until there is data to receive. If the channel is unbuffered, the sender blocks until the receiver has received the value. If the channel has a buffer, the sender blocks only until the value has been copied to the buffer; if the buffer is full, this means waiting until some receiver has retrieved a value.

您突出显示的句子就是您要查找的简单解释。

If the channel is unbuffered, the sender blocks until the receiver has received the value.

这是第 3 点的另一种说法:

A receive from an unbuffered channel happens before the send on that channel completes.

当您在无缓冲通道上发送时,发送方会阻塞,直到接收方获取值。这意味着 接收发生在发送完成之前

缓冲通道不同,因为值有去处。如果您仍然感到困惑,一个示例可能会有所帮助:

说我想在你家寄包裹:

  • 如果频道是缓冲的,你有地方让我留下包裹 - 也许是邮箱。这意味着我可以在您收到包裹(当您查看邮箱时)之前完成给您包裹(在频道上发送)的任务。
  • 如果频道没有缓冲,我只好在你家门口等你来帮我拿走包裹。在我完成交付给您的任务之前您已经收到了包裹。

For an unbuffered channel, the send operation(B) is blocked until the receiver gets ready to receive the value(A)? (like: B starts and does not return until A executes) Is it accurate?

是的。这是正确的。

For an unbuffered channel, the send operation(B) is blocked until the receiver gets ready to receive the value(A)? (like: B starts and does not return until A executes) Is it accurate?

是的,这是准确的。就像文档所说的那样,如果一个通道是无缓冲的,那么发送者将阻塞直到收到值。如果从未收到该值,您将遇到死锁并且程序将超时,如 this 示例:

var c = make(chan int)

func main() {
    c <- 0
    println("Will not print")
}

因此,无缓冲通道将阻塞发送操作,直到接收方准备好接收值,即使这需要一段时间。然而,对于缓冲通道,阻塞将发生在接收操作中。 This 示例显示无缓冲通道如何等待接收值,但缓冲通道不会:

package main

import "time"

var c chan int
var a string

func f() {
    time.Sleep(3)
    a = "hello, world"
    <-c // A
}
func test() {
    a = "goodbye"
    go f()
    c <- 0 // B
    println(a)
}

func main() {
    // Unbuffered
    c = make(chan int)
    test()

    // Buffered
    c = make(chan int, 1)
    test()
}

输出:

hello, world
goodbye

对我来说,在我阅读下一条规则后,我开始理解为什么必须那样做:

The kth receive on a channel with capacity C happens before the k+Cth send from that channel completes.

有点类似,你需要能够同步例程,所以接收方等待发送方。