2017-08-01 78 views
1

我試圖更好地理解golang頻道。在閱讀this article我與非阻塞玩弄發送和已經拿出下面的代碼:爲什麼在執行第一種情況時,此選擇總是運行默認情況?

package main 
import (
    "fmt" 
    "time" 
) 

func main() { 
    stuff := make(chan int) 
    go func(){ 
     for i := 0; i < 5; i ++{ 
      select { 
      case stuff <- i: 
       fmt.Printf("Sent %v\n", i) 
      default: 
       fmt.Printf("Default on %v\n", i) 
      } 
     } 
     println("Closing") 
     close(stuff) 
    }() 
    time.Sleep(time.Second) 
    fmt.Println(<-stuff) 
    fmt.Println(<-stuff) 
    fmt.Println(<-stuff) 
    fmt.Println(<-stuff) 
    fmt.Println(<-stuff) 
} 

這將打印:

Default on 0 
Default on 1 
Default on 2 
Default on 3 
Default on 4 
Closing 
0 
0 
0 
0 
0 

雖然我明白,只有0旨意得到印刷我不明白爲什麼第一次發送仍然會觸發select的default分支?

什麼是後面的選擇在這種情況下的行爲邏輯?

Example at the Go Playground

回答

4

你永遠不發送任何值stuff,你執行所有默認的情況下,你去任何在fmt.Println報表接收操作之前。如果沒有可以繼續的其他操作,則立即採取default的情況,這意味着您的循環將盡快執行並返回。

想要阻止的循環,所以你不需要default情況。你並不需要close末或者,因爲你沒有依靠封閉的通道疏通接收或從range條款打破。

stuff := make(chan int) 
go func() { 
    for i := 0; i < 5; i++ { 
     select { 
     case stuff <- i: 
      fmt.Printf("Sent %v\n", i) 
     } 
    } 
    println("Closing") 
}() 
time.Sleep(time.Second) 
fmt.Println(<-stuff) 
fmt.Println(<-stuff) 
fmt.Println(<-stuff) 
fmt.Println(<-stuff) 
fmt.Println(<-stuff) 

https://play.golang.org/p/k2rmRDP38f

還要注意的是最後的「已發送」和「關閉」行不打印,因爲你沒有其他的同步等待夠程來完成,但這並不影響這個例子的結果。

+0

所以我的例子'0'打印的是'int'的零值,而不是我認爲我會發送的值? – m90

+0

@ m90:是的,未緩衝的頻道已經關閉,所以沒有收到任何東西。 – JimB

2

只執行默認的情況下,由於對循環運行任何東西開始從通道讀取數據前的5倍。每一次,因爲沒有任何東西可以從頻道讀取,它會進入默認情況。如果有什麼可以從渠道中讀取,它會執行這種情況。

1

你的第一種情況下不執行。

這是你的程序在做什麼:

  1. 啓動夠程。
  2. 嘗試通過4通道,所有塊上發送0,因爲沒有讀取信道,所以通過降低到默認。
  3. 同時,在主要的goroutine,你睡覺一秒...
  4. 然後第二過後,試圖從通道讀取,但它是封閉的,所以你每次0

爲了得到你想要的行爲,你有兩個選擇:

  1. 使用一個緩衝信道,可容納所有數據的發送:

    stuff := make(chan int, 5) 
    
  2. 不要在您的select語句中使用default,這將導致每個發送等待,直到它可以成功。

哪個是首選取決於您的目標。對於這樣一個最低限度的例子,要麼可能不會更好或更糟。

+0

我不認爲第一次發送實際上是成功的。 https://play.golang.org/p/AN7c7SS98w – Gavin

+0

@Gavin:哦,當然你是對的。 – Flimzy

2

由於您使用的是非阻塞的'發送',stuff <- i只有在讀取器已經在等待讀取通道中的內容或通道有緩衝區時纔會執行。否則,'發送'將不得不阻止。

現在,由於您在從通道讀取的打印語句之前有一個time.Sleep(time.Second),直到1秒後纔會有通道讀取器。另一方面,goroutine在那段時間內完成執行,不發送任何東西。

由於fmt.Println(...)語句是從封閉通道讀取的,因此您會看到輸出中的全爲零。

+1

+1是唯一一個非常明確地指出需要有讀者已經在等待切換的人。其他人暗示這一點,但實際上並沒有這樣解釋,所以不能被誤解。 – RayfenWindspear

相關問題