单例模式恐怕是最为人熟知的一种设计模式了。它同样也是创建型模式的一种。当某个struct只允许有一个实例的时候,我们会用到这种设计模式。这个struct的唯一的实例被称为单例对象。下面是需要创建单例对象的一些场景:
单例对象通常在struct初始化的时候创建。通常,如果某个struct只需要创建一个实例的时候,会为其定义一个getInstance()方法,创建的单例实例会通过这个方法返回给调用者。
因为Go语言中有goroutines,它会给单例模式的应用带来一些麻烦。我们在构建单例模式的时候必须要考虑到在多个goroutines访问struct的getInstance()方法的时候应该返回相同的实例。下面的代码演示了如何正确的创建一个单例对象:
var lock = &sync.Mutex{}
type single struct {
}
var singleInstance *single
func getInstance() *single {
    if singleInstance == nil {
        lock.Lock()
        defer lock.Unlock()
        if singleInstance == nil {
            fmt.Println("Creting Single Instance Now")
            singleInstance = &single{}
        } else {
            fmt.Println("Single Instance already created-1")
        }
    } else {
        fmt.Println("Single Instance already created-2")
    }
    return singleInstance
}
以上的代码保证了single struct只会有一个实例。代码中有几处可以注意下:
getInstance()方法的起始处首先检查了下singleInstance是否为nil。这样每次调用getInstance()方法的时候可以避免执行“锁”操作。因为“锁”相关的操作比较耗资源,会影响性能,因此越少调用越好。singleInstance对象在“锁”作用区间内创建,可以避免goroutines的影响。singleInstance对象是否为空。这是因为可能会有多个goroutines通过第一次校验,二次校验可以保证只有一个goroutine创建单例,不然每个goroutine都有可能会创建一个single struct实例。完整代码在这里:
single.go
import (
	"fmt"
	"sync"
)
var lock = &sync.Mutex{}
type single struct {
}
var singleInstance *single
func GetInstance() *single {
	if singleInstance == nil {
		lock.Lock()
		defer lock.Unlock()
		if singleInstance == nil {
			fmt.Println("Creating Single Instance Now")
			singleInstance = &single{}
		} else {
			fmt.Println("Single Instance already created-1")
		}
	} else {
		fmt.Println("Single Instance already created-2")
	}
	return singleInstance
}
main.go
import (
	"fmt"
)
func main() {
	for i := 0; i < 100; i++ {
		go GetInstance()
	}
	// Scanln is similar to Scan, but stops scanning at a newline and
	// after the final item there must be a newline or EOF.
	fmt.Scanln()
}
输出内容:
Creating Single Instance Now Single Instance already created-1 Single Instance already created-2 Single Instance already created-1 Single Instance already created-1 Single Instance already created-1 Single Instance already created-1 Single Instance already created-1 Single Instance already created-1 Single Instance already created-2 Single Instance already created-2 ...
简单说明下:
singlestruct的实例。singleInstance对象是否为空的校验,它本来都有机会创建单例。除了锁+二次校验的方式,还有其它创建单例的方法,我们来看一下:
在init()函数中创建单例。因为一个包中每个文件的init()函数都只会调用一次,这样就可以保证只有一个实例会被创建。看下代码:
import (
	"fmt"
	"log"
)
type single struct {
}
var singleInstance *single
func init() {
	fmt.Println("Creating Single Instance Now")
	singleInstance = &single{}
}
func GetInstance() *single {
	if singleInstance == nil {
		log.Fatal("Single Instance is nil")
	} else {
		fmt.Println("Single Instance already created-2")
	}
	return singleInstance
}
这应该就是go语言中的懒汉式单例创建方法了。如果不介意过早创建实例造成的资源占用,推荐使用这种方法创建单例。
sync.Once中的代码会被保证只执行一次,这完全可以用来创建单例。代码如下:
import (
	"fmt"
	"sync"
)
var once sync.Once
type single struct {
}
var singleInstance *single
func GetInstance() *single {
	if singleInstance == nil {
		once.Do(
			func() {
				fmt.Println("Creating Single Instance Now")
				singleInstance = &single{}
			})
		fmt.Println("Single Instance already created-1")
	} else {
		fmt.Println("Single Instance already created-2")
	}
	return singleInstance
}
相比二次校验的方式,这里的代码可以说非常简洁了。这也是我非常建议使用的一种单例创建方式。
输出内容为:
Creating Single Instance Now Single Instance already created-1 Single Instance already created-2 Single Instance already created-2 Single Instance already created-1 Single Instance already created-2 Single Instance already created-1 Single Instance already created-1 Single Instance already created-2 Single Instance already created-1 Single Instance already created-2 Single Instance already created-1 Single Instance already created-2 Single Instance already created-2 Single Instance already created-2 Single Instance already created-2 Single Instance already created-2 Single Instance already created-2 Single Instance already created-2 Single Instance already created-2 ...
简单说明下:
代码已上传至GitHub:zhyea / go-patterns / singleton-pattern
End!
原文:https://www.cnblogs.com/amunote/p/15253251.html