2017-10-19 57 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緩衝通道和無緩衝通道的好例子,現在很清楚。還有一個有趣的超時示例。謝謝! –