This page looks best with JavaScript enabled

Go言語の sync.Once について

 ·   2 min read

ネットでGo言語の sync.Once についてつらつらと調べるていると

  • 1度だけ実行する処理を書くことができる
  • 2度目の呼び出しは実行されない

という紹介が多くあり、これは間違ってはいないし大変便利な機能であることがわかる。
ちなみにパッケージのドキュメントによれば、

Do calls the function f if and only if Do is being called for the first time for this instance of Once. if once.Do(f) is called multiple times, only the first call will invoke f, even if f has a different value in each invocation. A new instance of Once is required for each function to execute.
via package sync


ということで、要するに
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
func main() {
    once := sync.Once{}

    for i := 0; i < 5; i++ {
        once.Do(func(){
            fmt.Printf("Initialize by %v\n", i)
        })
        fmt.Printf("done %v\n", i)
    }
}

の実行結果は

Initialize by 0
done 0
done 1
done 2
done 3
done 4

となる。
sync.Once.Do()で呼び出す中身がなんだろうと1つの sync.Once インスタンスでは1回だけ実行される。

だがしかし、goroutine をつかって、sync.Once.Do() を同時に実行するとどうなるのか。
このことについて解説している文献があまりなかったので書いておく。

わかりやすいように once.Do() の完了に3秒以上かかるようにしてみる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
func main() {
    once := sync.Once{}
    wg := sync.WaitGroup{}

    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(n int) {
            defer wg.Done()
            once.Do(func(){
    	        fmt.Printf("Initialize by %v\n", n)
                time.Sleep(time.Second * 3)
    	    })
            fmt.Printf("done %v\n", n)
        }(i)
    }
    wg.Wait()
    fmt.Println("all done")
}

の実行結果は

Initialize by 4
(3秒後)
done 4
done 0
done 2
done 1
done 3
all done

のようになる。
つまり、 sync.Once.Do() は同時に実行すると最初の呼び出しが終わるまでブロックされる。
もちろんブロックするだけで2回目以降の処理は実行されない。

パッケージのドキュメントによれば、

Because no call to Do returns until the one call to f returns, if f causes Do to be called, it will deadlock.
via package sync


のように書いてあり、これは大変重要なことだと思う。

まとめ

sync.Once

  • スレッドセーフで、
  • ただ1回限りの実行を保証し、
  • その1回の実行完了を待つ

ということができる機能。

おかげさまで、「時間のかかる初期化処理を待つ」というようなコードは改めて書く必要がなかった、という話。
(もちろんプログラムの初期処理においては init() で事足りる場合が多いが)

参考: https://golang.org/src/sync/once.go

Share on

Avatar
WRITTEN BY
northeye
Takuo Kitame. A Software Engineer.