1 | package main |
Golang的单例模式核心在于sync.Once
构造体中的Do(f func())
方法。官方文档对Do(f func())
的说明是Do方法只会在sync.Once
对象第一次调用Do方法时真正执行f函数,多次调用Do方法时,即使f函数已经被改变也不会再次调用f函数。想多次调用必须创建新的sync.Once
对象。
代码中其实有一个可能是新旧golang版本差异的问题,在我看的golang书籍和文章中,有的地方会写到当一个函数的receiver为类型的指针时,在调用该函数时必须使用receiver类型指针变量调用,并给出一个值变量与指针变量可以调用什么类型的receiver函数的表格,使用值变量调用指针receiver函数会被编译器提示错误。但是经我测试在go1.12.4版本已经没有这样的要求,如下面源代码所示,Do
函数的receiver为Once
的指针,但是声明sync.Once
值变量同样可以调用。
1 | // sync库源码 |
从Once
和Do(f func())
的源码可以看到Once
结构体有一个done属性,当第一次执行时,Once
对象的done属性在创建时被初始化为初始,即为0。当第一次执行Do
时会同步执行f函数并把done属性通过atomic
库执行原子操作设为1,此后只要done属性为1,则不执行f函数。