【GO】Channelsを理解する


Golangを使っているといつの間にか使っているチャンネル。

なんとなくで使っていたので1から自分で書くと色々つまずいた。

動作確認をしながら理解を深めてみます。

ツアー:tour.golang.org/concurrency

ソース:runtime/chan.go

チャンネル作成と送受信

チャンネルはルーチン(golangのスレッド)間の送受信を行うためのものです。

送信、受信処理を始めると他のルーチンの送受信を待ちます。

 

なのでメインルーチンのみで通信待機状態にしてしまうとエラーが出ます。

fatal error: all goroutines are asleep - deadlock!

この場合、受信待ちが発生して解決しないのでデッドロックが発生します。

デッドロックを検知するとエラーにしてくれる優しさ。

 

チャンネルにはキャパシティーを設定できます。

送信したものをFIFO(入れた順に取り出す)で受信しています。

うまく使えば送受信の待ち時間を減らすことが出来ます。

 

チャンネルは以下の状況で通信待ちが発生します。

  • 何も入ってない状態での受信 <-chan
  • 容量いっぱいの時の送信 chan<-

容量0の場合で受信させると必ず待機状態になるので、よくサンプルコードでメインルーチンを終わらせないための処理として使われてます。

ルーチン間の通信

チャンネル待機して、受信したら出力するだけのルーティンを起動。

受け取り前にメインルーチンが終わらないようにSleepで遅延させています。

 

基本的に受信側はループ内で待機する書き方になります。

 

ところで受信ルーチンを3つ動かすとどうなるか。

送信値1,2,3は1つづつ出力されますが、順番やどのルーチンで受信するかは実行ごとに変わります。

これはgoroutineがParallel(並列)ではなくConcurrent(平行)処理なためです。

 

また、チャンネルには受信専用 <-chan と送信専用 chan<- もあります。

これ単体で見るとどうしようもないですが、どちらもchanを受け入れるのでこんな風に書くことができます。

チャンネルとして作って送信、受信しかできないチャンネルを渡す(返す)ことで意図しないコードを書けないようになりました。

チャンネルを閉じる

使い終わったチャンネルは閉じる必要があります。

閉じたチャンネルは通信待機しなくなり、送信しようとするとpanic: send on closed channelエラーになります。

受信側は第2引数で閉じていないかどうか取得可能です。

中身が残っている場合にはtrueとなるので全ての通信が終わってからfalse判定。

 

先ほどの送受信関数を書き換えてみるとこんな感じです。

 

このcloseしているかどうかの判定は単体チャンネルの場合for rangeが便利。

 

複数の受信待機する場合は複数のルーチンを走らせるかselectで処理を分ける。


コメントを残す

メールアドレスが公開されることはありません。