Golang覚書
概要
make(chan ...)でキャパを指定しないとunbufferedになる
- キャパを指定しないと、適度なデフォルト値でもなく、1でもなく、0なので。
- 同期処理用ということ。非同期プログラミングにおいては、むしろマイナー用途かも。
channelのバッファのキャパは送信側が管理すること
- 溢れるかどうかは、受信側ではどうしようもないので、送信側がなんとかする。
- 原則として、channelが溢れたらchannelを殺す(closeする)(しかない)。
- channelのバッファはそのつもりで十分確保すること。
送信側はchannel溢れを直ちに検知できるよう(間違ってもブロックなどせぬよう)、送信時は原則として以下のイディオムを使う。
select { case chChild <- value: default: /* Kill the child */ }
channelは原則として送信側が閉じること
- closed channelに送信すると実行時例外が発生してしまうので、送信側でclosedか否かの管理をする。
- 受信側では例外など出ずに自然にcloseをハンドリングできる。
送信側だと、panic-recoverを用いた以下のイディオムでハンドリングは可能。もしどうしても必要なら。原則として避ける。
func sendToUnstableChannel() { defer func() { recover() } ch <- v }
closeはブロックしない
- cap=10のchanに10個のデータが既に詰まってる状況だと、更にsendしようとするとブロックするが、closeはブロックしない。
- closeは非同期処理において安全(ブロックしない)処理だと言える。
channel受信は原則として、特別形式の受信式もしくはfor-range受信で行うこと
以下のようにすると、chがcloseされた場合、無限busyループするので注意。
Loop: for { select { case v := <-ch: /* Use a received value */ case <-timeout: break Loop } }
正しくは特別形式で受信
for { select { case v, ok := <-ch: if !ok { break Loop } /* Use a received value */ case <-timeout: break Loop } }
もしくはfor-range受信(タイムアウトが設定できないのが難点)
for v := range ch { /* Use a received value */ }
ループの途中でコレクションの要素を削除する場合の注意
mapの場合は
delete文を使えば問題無い
for k, v := range m { if cond(v) { delete(m, k) } }
sliceの場合は
- downwardループを使う
- append式での要素削除は、i以降の要素をshiftするため、i以降の要素に対してループを継続すると、おかしなことになる。
downwardなら問題は起こらない。
for i := len(s) - 1; i >= 0; i-- { if cond(s[i]) { s = append(s[:i], s[i+1:]...) } }
原則としてbusyループは避けること
- Goroutineは任意の箇所でプリエンプト出来る(される)わけではないので、下手をするとCPUを占有する可能性がある。
- よほど理由があり問題無いと分かっているのでなければ、busyループは避ける。
- ループがbusyループになりうるのではないか、気を付ける。