Siburlog

SiblogだとSI BlogっぽいのでSiburlogとする

Golang覚書

概要

  • 最近Golang書いていて気付いた箇所・嵌った箇所や、自分の書き方の方針など。
  • 主旨はツッコミ待ち。会社で一人で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ループになりうるのではないか、気を付ける。