关于设计模式:Go中的单例Singleton

Singleton in go

如何在Go编程语言中实现单例设计模式?


抛开实现singleton模式是否是一个好主意的争论,下面是一个可能的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package singleton

type single struct {
        O interface{};
}

var instantiated *single = nil

func New() *single {
        if instantiated == nil {
                instantiated = new(single);
        }
        return instantiated;
}

singleinstantiated是私有的,但New()是公共的。因此,在不经过New()的情况下,不能直接实例化single,它用私有的布尔值instantiated跟踪实例化的次数。调整single的定义以适应口味。

然而,正如其他一些人所指出的,这不是线程安全的,除非您只是在init()中初始化单例。一个更好的办法是利用sync.Once为你做艰苦的工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package singleton

import"sync"

type single struct {
        O interface{};
}

var instantiated *single
var once sync.Once

func New() *single {
        once.Do(func() {
                instantiated = &single{}
        })
        return instantiated
}

另见,哈桑J的建议只是把一个包裹当作一个单件。最后,要考虑其他人的建议:单例常常是一个有问题的实现的指示器。


在试图找到一种方法屈从于你的意志之前,你可能需要看一些文章:

  • 单打-反模式!

  • 单身是病态的骗子

  • 单子现象的根本原因。

总的来说,随着时间的推移,人们发现单例开发并不是最理想的,而且imho尤其是当你尝试进行任何测试驱动的开发时:在许多层面上,它们和全局变量一样糟糕。

[免责声明:我知道这不是对您问题的严格回答,但它确实是相关的]


只需将变量和函数放在包级别。

另请参见类似的问题:如何在Python中生成单例


我认为,在一个并发的世界中,我们需要更清楚地认识到这些行不是原子地执行的:

1
2
3
if instantiated == nil {
    instantiated = new(single);
}

我会按照@markermer的建议使用"同步"软件包。

1
2
3
4
5
6
7
8
9
10
11
12
13
import"sync"

type MySingleton struct {

}

var _init_ctx sync.Once
var _instance *MySingleton

func New() * MySingleton {
     _init_ctx.Do( func () { _instance = new(MySingleton) }  )
     return _instance
}


最好的方法是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 package singleton

 import"sync"

 type singleton struct {
 }

 var instance *singleton
 var once sync.Once

 func GetInstance() *singleton {
     once.Do(func() {
         instance = &singleton{}
     })
     return instance
 }

你应该阅读这个链接


您可以使用ONCE包进行初始化:

这将确保只调用一次init方法。


您应该知道,Once.Do对于只执行一次代码是认真的。这意味着,代码总是只执行一次,即使它可能会惊慌失措:

来自https://golang.org/pkg/sync/once.do

If f (note: the once logic) panics, Do considers it to have returned; future calls of Do return without calling f.

我使用互斥来确保全局配置变量的唯一初始化,以克服此限制:

  • https://golang.org/pkg/sync/互斥
  • https://gobyexample.com/mutex

只需要一个静态的、最终的、常量的、全局的、应用程序范围的对象实例。

然而,这与OO范式相矛盾。它的使用应该局限于原语和不变的对象,而不是可变的对象。