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
}