2017-10-19 56 views
1

学习golang,我当试图理解下面的内存模型规范描述的信道通信的有点糊涂了:通道上如何理解golang内存模型中的通道通信规则?在路上

  1. 一个发送发生之前对应的从通道接收完成。
  2. 通道的关闭发生在因为通道关闭而返回零值的接收之前。
  3. 来自无缓冲通道的接收发生在该通道上的发送完成之前。
  4. 在容量为C的信道上的第k个接收发生在从该信道发送的第k + C个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 spec中发现了以下声明,但我的理解仍然存在差异......所以有人可以用简单直接的方式解释这个吗?

接收器总是阻塞,直到有数据要接收。 如果通道 未缓冲,发送方将阻塞,直到接收方收到值 。如果通道有一个缓冲区,发送方只会阻塞,直到 值被复制到缓冲区;如果缓冲区已满,则表示 等待某个接收方检索到值。

+1

“发生之前” HB有着非常特殊的意义,仅仅是松散与线性时间内的传统“之前”有关。你可以有两个事件'x'和'y',两个关系保持:'x HB y'和'y HB x'。规则1和3一起粗略地说:“在没有缓冲的通道上发送和接收正确的同步和工作就像你期望的一样(不管CPU如何重组你的指令,你的内存缓存多么积极,什么不缓存)。” – Volker

+0

@Volker嗯...仍然困惑为什么'x HB y'和'y HB x'都成立,哈哈。可能,我需要将一个事件'e'分成不同的原子阶段,比如开始,处理和结束,在为了正确理解这个“发生在......之前”的术语......感谢! –

+2

HB并不意味着实际发生的事情早于其他事物,它只是一个方便的助记符。哪些内存操作是可见的,这是一个_model_,而不是现实,HB建立的是同步保证,它说_nothing_关于实际发生的事情 – Volker

回答

4

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

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

这是说3点的另一种方式:

一个接收来自该通道完成发送前的缓冲通道发生。

当在未缓冲的信道发送时,发送方直到接收机已采取的值。这意味着接收发生在发送完成之前。

缓冲通道是不同的,因为值有某处去。如果你仍然感到困惑,一个例子可以帮助:

说,我想在你的房子留下一个包:

  • 如果通道被缓冲,你有地方我离开包 - 也许一个邮箱。这意味着我可以在收到邮件之前完成我的任务(在频道上发送邮件)(当您检查邮箱时)。
  • 如果频道没有缓冲,我必须在您的前门等待,直到您离开我的包裹。在我完成将其交付给您的任务之前,您会收到包裹。

对于未缓冲的信道,则发送操作(B)被阻塞,直到接收器准备好接受的值(A)? (如:B启动并且不会返回,直到A执行)它是否准确?

是的。这是对的。

+1

很好的例子,这非常有用! NKS! –

1

对于无缓冲的通道,发送操作(B)被阻塞,直到接收器准备好接收值(A)? (如:B启动并且不会返回,直到A执行)它是否准确?

是的,这是准确的。就像文档所说的,如果一个频道没有缓存,那么发件人将阻塞,直到收到该值。如果该值没有收到你会得到一个僵局,该方案将在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 
+0

我认为你有错误的方式'A'和'B'。 OP的声明是正确的 - 'B'确实阻塞,直到'A'发生(永远如果'A'永远不会发生)。 –

+0

@TimothyJones是的,你是对的。我提供的例子反映了这一点,但我的陈述没有。 –

+0

@JackGore缓冲通道和无缓冲通道的好例子,现在很清楚。还有一个有趣的超时示例。谢谢! –