Go
golang

逆引きgolang

More than 1 year has passed since last update.

随時更新

Goroutine & Channel

goroutineの終了を待つ

(http://jxck.hatenablog.com/entry/20130414/1365960707 が良記事です)

1つのgoroutineを待つだけならchannelを使えばok:

main.go
package main

import "log"

func main() {
  ch := make(chan int)
  go func(ch chan int) {
    // do something

    log.Printf("end of goroutine")
    ch <- 1
  }(ch)
  log.Printf("waiting for goroutine")
  <- ch
  log.Printf("end of main")
}

複数のgoroutineを待つ場合はsync.WaitGroupを使うらしいです:

main.go
package main

import (
  "log"
  "sync"
)

func main() {
  var wg sync.WaitGroup
  for i := 0; i < 3; i++ {
    wg.Add(1) // goroutineの数だけAdd()
    go func(i int) {
      defer wg.Done() // goroutineを抜けたらDone()
      log.Printf("goroutine: %d", i)
    }(i)
  }
  log.Printf("waiting for goroutines")
  wg.Wait() // ここでblockして待つ
  log.Printf("end of main")
}

ちなみに細かいことですが、Done()が呼ばれたそばから終了するので、複数のdeferを呼ぶ場合は、Done()を"最初に"書いておかないと他のdeferが呼ばれる前にプロセスが終了してしまう可能性があります (deferはLIFOで実行されます: http://blog.golang.org/defer-panic-and-recover)

Build

debug / release buildを分ける

build tagを使います。(例えば)env packageにenv_debug.goとenv_release.goをつくって、それぞれにbuild条件を書くと、build tagにmatchするファイルだけがbuildされます。

env/env_debug.go
// +build !release

package env

const DEBUG = true
env/env_release.go
// +build release

package env

const DEBUG = false

このとき、"// +build ..."は1行目に書く必要があって、且つ、1行空行が必要です。で、このpackageをimportして振る舞いを分けます。

main.go
package main

import (
  "github.com/user/app/env"
)

func main() {
  if env.DEBUG {
    // ...
  }
}

debug buildは

$ go build github.com/user/app

で、release buildは

$ go build -a -tags=release github.com/user/app

になります (build tagを変えただけの場合は、正しく全てのライブラリがbuildし直されない可能性があるので、-aが無難です)。

その他

呼び出し元のfuncを取得する

pc, _, _, _ := runtime.Caller(1)
caller_name := runtime.FuncForPC(pc).Name();

で出来ます。DEBUGフラグとかがあれば:

debug.go
import (
  "log"
  "runtime"
  "github.com/user/app/env"
)

func Debug(format string, args ...interface{}) {
  if env.DEBUG {
    pc, _, _, _ := runtime.Caller(1)
    caller_name := runtime.FuncForPC(pc).Name();

    log.Printf(caller_name + ": " + format, args...)
  }
}

で、デバッグログが出力しやすくなる気がします。

packageのprefixを省略する

main.go
package main

import (
  "log"
)

func main() {
  log.Printf("hello")
}

とかの"log."を省略するには

import (
  . "log"
)

とすると

Printf("hello")

と書けます、まめ知識です。同様に、

import (
  foo "log"
)

とすれば

foo.Printf("hello")

でアクセスできます。

See Also, and Reference Sites