2017-08-01 12 views
1

私はゴランのチャンネルをよりよく理解しようとしています。最初のケースが実際に実行されたときに、常にこの選択がデフォルトのケースを実行するのはなぜですか?

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 sが印刷されますことを理解しないがthis articleを読んでいる間、私は非ブロッキング送信し、次のコードが出ているといじりますよ最初の送信が依然としてdefaultのブランチをトリガーしている理由がわかりません。

この場合のselectの動作の背景にある論理は何ですか?

Example at the Go Playground

答えて

4

stuffに値を送信することはありません。fmt.Printlnステートメントのいずれかの受信操作に入る前に、すべてのデフォルトのケースを実行します。 defaultケースは、実行可能な他の操作がない場合はすぐに実行されます。つまり、ループが実行され、できるだけ早く返されます。

ループをブロックしたいので、defaultのケースは必要ありません。クローズドチャネルに依存していないため、range句の受信をブロック解除したり破棄したりする必要がないため、最後にcloseは必要ありません。あなたはゴルーチンが終了するのを待っている他の同期を持っていないので、最後の「送信済み」と「クロージング」ラインは、印刷されません、しかし、効果ないことも

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

私の印刷例は 'int'のゼロ値で、私が送ると思う値ではありません。 – m90

+0

@ m90:はい、バッファされていないチャネルは既に閉じられているため、何も受信されませんでした。 – JimB

2

何でもチャンネルからの読み込みを開始する前にループのために5回を実行するためにのみ、デフォルトのケースを実行します。通過するたびに、何もチャンネルから読み取ることができないので、デフォルトの場合に進みます。チャネルから何かを読み取ることができる場合、その場合は実行されます。

1

あなたの最初のケースは実行されていません。

はここにあなたのプログラムが何をするかです:

    ゴルーチンを起動し
  1. 04をチャネルに送信しようとしましたが、チャネルは何も読み取られていないため、すべてブロックされます。したがって、デフォルトになります。その後
  2. 一方、メインゴルーチンに、あなたは1秒間に眠っている...
  3. 第二は、チャネルから読み取るしようとする試みが経過したが、それが閉じているので、あなたが0を毎回取得した後。

    1. 使用あなたが送ったデータのすべてを保持することができ、バッファチャンネル、::

      stuff := make(chan int, 5) 
      
    2. しないでください

    ご希望の動作を取得するには、次の2つの選択肢がありselectステートメントでdefaultを使用します。これにより、成功するまで各送信が待機します。

これは、あなたの目標によって異なります。このような最小限の例では、おそらくそれほど良くないか悪いです。

+0

私は最初の送信が実際に成功しているとは思わない。 https://play.golang.org/p/AN7c7SS98w – Gavin

+0

@ギャビン:ああ、もちろんあなたは正しい。 – Flimzy

2

ノンブロッキングの「送信」を使用しているため、stuff <- iは、実際にはチャンネル上で物事を読むのを待っている読者がいる場合、またはチャンネルにバッファがある場合にのみ実行されます。そうでなければ、 'send'をブロックする必要があります。

チャンネルから読み取った印刷文の前にtime.Sleep(time.Second)があるので、1秒後までチャンネルのリーダーはありません。一方、goroutineはその時間内に実行を終了し、何も送信しません。

fmt.Println(...)ステートメントがクローズドチャネルから読み取っているため、出力にすべてのゼロが表示されています。

+1

+1は、既にハンドオフを待っているリーダーが必要であることを非常に明示的に指摘する唯一の人だからです。他のものはこれを暗示していますが、実際にこのように説明していないので誤解できません。 – RayfenWindspear

関連する問題