ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

GoLang设计模式04 - 单例模式

2022-01-26 18:35:31  阅读:209  来源: 互联网

标签:already created single GoLang Instance Single singleInstance 单例 设计模式


单例模式恐怕是最为人熟知的一种设计模式了。它同样也是创建型模式的一种。当某个struct只允许有一个实例的时候,我们会用到这种设计模式。这个struct的唯一的实例被称为单例对象。下面是需要创建单例对象的一些场景:

  • 数据库实例:一般在开发中,对于一个应用,我们通常只需要一个数据库对象实例
  • 日志实例:同样,对于一个应用来说,日志操作对象也只需要一个实例

单例对象通常在struct初始化的时候创建。通常,如果某个struct只需要创建一个实例的时候,会为其定义一个getInstance()方法,创建的单例实例会通过这个方法返回给调用者。

因为Go语言中有goroutines,它会给单例模式的应用带来一些麻烦。我们在构建单例模式的时候必须要考虑到在多个goroutines访问struct的getInstance()方法的时候应该返回相同的实例。下面的代码演示了如何正确的创建一个单例对象:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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只会有一个实例。代码中有几处可以注意下:

  1. getInstance()方法的起始处首先检查了下singleInstance是否为nil。这样每次调用getInstance()方法的时候可以避免执行“锁”操作。因为“锁”相关的操作比较耗资源,会影响性能,因此越少调用越好。
  2. singleInstance对象在“锁”作用区间内创建,可以避免goroutines的影响。
  3. 在获取到“锁”资源后,程序中又一次校验了singleInstance对象是否为空。这是因为可能会有多个goroutines通过第一次校验,二次校验可以保证只有一个goroutine创建单例,不然每个goroutine都有可能会创建一个single struct实例。

完整代码在这里:

single.go

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 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

1 2 3 4 5 6 7 8 9 10 11 12 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() }

  输出内容:

1 2 3 4 5 6 7 8 9 10 11 12 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 ...

简单说明下:

  • 输出内容中只有一行“Creating Single Instance Now”这样的输出,这说明只有一个goroutine能够创建一个singlestruct的实例。
  • 输出内容中有多行“Single Instance already created-1”,说明有多个goroutines通过第一次检查singleInstance对象是否为空的校验,它本来都有机会创建单例。
  • 最后输出的都是“Single Instance already created-2”,意味着单例已创建完成,之后的goroutines都无法再通过首次校验。

除了锁+二次校验的方式,还有其它创建单例的方法,我们来看一下:

基于init()函数

init()函数中创建单例。因为一个包中每个文件的init()函数都只会调用一次,这样就可以保证只有一个实例会被创建。看下代码:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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

sync.Once中的代码会被保证只执行一次,这完全可以用来创建单例。代码如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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 }

相比二次校验的方式,这里的代码可以说非常简洁了。这也是我非常建议使用的一种单例创建方式。

输出内容为:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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 ...

简单说明下: 

  • 输出的内容和二次校验的方式差不多,仍然存在多行“Single Instance already created-1”这样的输出,说明有多个goroutine通过了if校验
  • 输出内容中只有一行“Creating Single Instance Now”,说明只有一个goroutine能够创建实例。

代码已上传至GitHub:zhyea / go-patterns / singleton-pattern

End!

 

转 https://www.cnblogs.com/amunote/p/15253251.html

标签:already,created,single,GoLang,Instance,Single,singleInstance,单例,设计模式
来源: https://www.cnblogs.com/wl-blog/p/15847787.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有