LoginSignup
44
41

More than 5 years have passed since last update.

Goでスレッドセーフなシングルトン

Last updated at Posted at 2015-06-14

Goのシングルトンで検索すると、以下のような内容でシングルトンを実現していることが多いです。

singleton.go
package singleton

type single struct {
    // Some fields
}

var sharedInstance *single

func GetInstance() *single {
    if sharedInstance == nil {
        sharedInstance = &single{ /* 初期化 */ }
    }

    return sharedInstance
}

しかし、これでは複数スレッドからほぼ同時にGetInstance()が呼ばれると、複数のインスタンスが生成されてしまいます。
例として、以下のようにGetInstance()を変更し、非同期で実行してみます。

singleton.go
package singleton

import (
    "fmt"
    "time"
)

/* 省略 */

func GetInstance() *single {
    if sharedInstance == nil {
        fmt.Println("create new instance.")
        time.Sleep(1000) // 同時にGetInstanceが呼ばれるようわざと時間をかける
        sharedInstance = &single{ /* 初期化 */ }
    }

    return sharedInstance
}
main.go
package main

import (
    "./singleton"
)

func main() {
    ch := make(chan interface{})
    go run(ch)
    go run(ch)
    go run(ch)
    <- ch
}

func run(ch chan interface{}) {
    ch <- singleton.GetInstance()
}

結果は以下のようになります。

$ go run main.go
create new instance.
create new instance.
create new instance.

したがって、Goにおいてスレッドセーフなシングルトンを実現したいのであれば、以下のようにするといいと思います。

singleton.go
package singleton

type single struct {
    // Some fields
}

var sharedInstance *single = newSingle()

func newSingle() *single {
    // 何かしらの初期化処理
    return &single{ /* 初期化 */ }
}

func GetInstance() *single {
    return sharedInstance
}

特に初期化処理が必要ないのであれば、以下のようにもっと簡潔にすることもできます。

singleton.go
package singleton

type single struct {
    // Some fields
}

var sharedInstance *single = &single{ /* 初期化 */ }

func GetInstance() *single {
    return sharedInstance
}
44
41
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
44
41