7

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

  1. 通道上的发送发生在该通道的相应接收完成之前。
  2. 通道的关闭发生在接收返回零值之前,因为通道已关闭。
  3. 来自无缓冲通道的接收发生在该通道上的发送完成之前。
  4. 容量为 C 的通道上的第 k 次接收发生在该通道的第 k+C 次发送完成之前。

前两条规则清晰易懂,而我对第三条规则感到困惑,这似乎与其他规则相反......我是否错过了无缓冲通道的特别之处?或者我是否正确如果我像下面的规范中的示例一样采用它:

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 开始,直到 A 执行才返回)它准确吗?

我在Effective Go 规范中找到了以下陈述,但与我的理解仍然存在差异......那么有人可以用简单明了的方式解释一下吗?

接收器总是阻塞,直到有数据要接收。如果通道没有缓冲,发送方会阻塞,直到接收方收到该值。如果通道有缓冲区,发送方只会阻塞,直到值被复制到缓冲区;如果缓冲区已满,这意味着要等到某个接收器检索到一个值。

4

3 回答 3

16

您突出显示的句子是您正在寻找的简单解释。

如果通道没有缓冲,发送方会阻塞,直到接收方收到该值。

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

来自无缓冲通道的接收发生在该通道上的发送完成之前。

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

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

假设我想在你家留下一个包裹:

  • 如果频道被缓冲,你有地方让我离开包裹——也许是邮箱。这意味着我可以在您收到包裹(当您检查邮箱时)之前完成我的任务,即在您收到包裹之前(在频道上发送)。
  • 如果频道没有缓冲,我必须在你的前门等,直到你来把包裹从我身上拿走。在我完成交付给您的任务之前,您会收到包裹。

对于无缓冲通道,发送操作(B)被阻塞,直到接收器准备好接收值(A)?(如:B 开始,直到 A 执行才返回)它准确吗?

是的。这是对的。

于 2017-10-19T04:59:54.890 回答
1

对于无缓冲通道,发送操作(B)被阻塞,直到接收器准备好接收值(A)?(如:B 开始,直到 A 执行才返回)它准确吗?

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

var c = make(chan int)

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

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

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
于 2017-10-19T05:01:12.993 回答
0

对我来说,在我阅读下一条规则后,为什么必须这样开始变得有意义:

容量为 C 的通道上的第 k 次接收发生在该通道的第 k+C 次发送完成之前。

有点相同,您需要能够同步例程,因此接收方等待发送方。

于 2021-06-29T01:17:51.713 回答