关闭

设计模式-单例模式(Go语言描述)

标签: 设计模式go语言
11260人阅读 评论(13) 收藏 举报
分类:

这篇博客我们继续来看设计模式,今天带来的是一个最简单而且最常用的模式-单例模式。那什么是单例模式呢?相信大家最它最熟悉不过了,那我们就来快速的了解一下它的定义。

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

这个解释足够简单。说白了就是假如我们希望我们在我们的系统中该类仅仅存在1个或0个该类的实例。虽然单例模式很简单,但是熟悉java的同学可能了解,单例模式有很多写法,懒汉式饿汉式双重锁。。。 这么多形式,难道有什么目的?确实,不过他们的目的很明确,就是保证在一种特殊情况下的单例-并发

ok,既然了解了单例模式,那下面我们就开始用代码描述一下单例模式。首先是最简单的单例,这里我们并不去考虑并发的情况

package manager
import (
    "fmt"
)

var m *Manager

func GetInstance() *Manager {
    if m == nil {
        m = &Manager {}
    }
    return m
}

type Manager struct {}

func (p Manager) Manage() {
    fmt.Println("manage...")
}

这就是一个最简单的单例了,对于Manager结构体,我们提供了一个GetInstance函数去获取它的实例,这个函数中首先去判断m变量是否为空,如果为空才去赋值一个Manager的指针类型的值,一个小小的判断,就保证了我们在第第二次调用GetInstance的时候直接返回m,而不是重新获取Manager的实例,进而保证了唯一实例。

上面的代码确实简单,也实现了最简单的单例模式,不过大家有没有考虑到并发这一点,在并发的情况下,这里是不是还可以正常工作呢? 来,先跟着下面的思路走一走,来看看问题出现在哪。

现在我们是在并发的情况下去调用的 GetInstance函数,现在恰好第一个goroutine执行到m = &Manager {}这句话之前,第二个goroutine也来获取实例了,第二个goroutine去判断m是不是nil,因为m = &Manager{}还没有来得及执行,所以m肯定是nil,现在出现的问题就是if中的语句可能会执行两遍!

在上面介绍的这种情形中,因为m = &Manager{}可能会执行多次,所以我们写的单例失效了,这个时候我们就该考虑为我们的单例加锁啦。

这个时候我们就需要引入go的锁机制-sync.Mutex了,修改我们的代码,

package manager
import (
    "sync"
    "fmt"
)

var m *Manager
var lock *sync.Mutex = &sync.Mutex {}

func GetInstance() *Manager {
    lock.Lock()
    defer lock.Unlock()
    if m == nil {
        m = &Manager {}
    }
    return m
}

type Manager struct {}

func (p Manager) Manage() {
    fmt.Println("manage...")
}

代码做了简单的修改了,引入了锁的机制,在GetInstance函数中,每次调用我们都会上一把锁,保证只有一个goroutine执行它,这个时候并发的问题就解决了。不过现在不管什么情况下都会上一把锁,而且加锁的代价是很大的,有没有办法继续对我们的代码进行进一步的优化呢? 熟悉java的同学可能早就想到了双重的概念,没错,在go中我们也可以使用双重锁机制来提高效率。

package manager
import (
    "sync"
    "fmt"
)

var m *Manager
var lock *sync.Mutex = &sync.Mutex {}

func GetInstance() *Manager {
    if m == nil {
        lock.Lock()
        defer lock.Unlock()
        if m == nil {
            m = &Manager {}
        }
    }

    return m
}

type Manager struct {}

func (p Manager) Manage() {
    fmt.Println("manage...")
}

代码只是稍作修改而已,不过我们用了两个判断,而且我们将同步锁放在了条件判断之后,这样做就避免了每次调用都加锁,提高了代码的执行效率。

这获取就是很完美的单例代码了,不过还没完,在go中我们还有更优雅的方式去实现。单例的目的是啥?保证实例化的代码只执行一次,在go中就中这么一种机制来保证代码只执行一次,而且不需要我们手工去加锁解锁。对,就是我们的sync.Once,它有一个Do方法,在它中的函数go会只保证仅仅调用一次!再次修改我们的代码,

package manager
import (
    "sync"
    "fmt"
)

var m *Manager
var once sync.Once

func GetInstance() *Manager {
    once.Do(func() {
        m = &Manager {}
    })
    return m
}

type Manager struct {}

func (p Manager) Manage() {
    fmt.Println("manage...")
}

代码更简单了,而且有没有发现-漂亮了!Once.Do方法的参数是一个函数,这里我们给的是一个匿名函数,在这个函数中我们做的工作很简单,就是去赋值m变量,而且go能保证这个函数中的代码仅仅执行一次!

ok,到现在单例模式我们就介绍完了,内容并不多,因为单例模式太简单而且太常见了。我们用单例的目的是为了保证在整个系统中存在唯一的实例,我们加锁的目的是为了在并发的环境中单例依旧好用。不过虽然单例简单,我们还是不能任性的用,因为这样做实例会一直存在内存中,一些我们用的不是那么频繁的东西使用了单例是不是就造成了内存的浪费?大家在用单例的时候还是要多思考思考,这个模块适不适合用单例!

11
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

golang设计模式(1)单例模式

做软件开发工程师两三年了,接触到的第一种设计模式就是单例模式,记得当年第一份工作第一个任务就是用C语言写一个网络抓包的工具。那时候坐旁边的同事就问,你们两写的这个工具有用到什么设计模式吗?当时一脸懵,...
  • m0_38132420
  • m0_38132420
  • 2017-09-25 09:58
  • 257

大话设计模式 -- Golang实现

DesignPatternUse GO language to achieve 23 design patterns 该项目是拜读《大话设计模式》后,学习Golang语言实现的,有很多不足之处,有待...
  • aa779025105
  • aa779025105
  • 2017-03-13 11:46
  • 771

设计模式-策略模式(Go语言描述)

好久没有更新博客了,最近也是在忙着充电,从今天这篇博客开始,我们来了解一下设计模式。设计模式那什么是设计模式呢?首先来看看我从百科上copy下来的概念吧。 设计模式/软件设计模式(Design p...
  • qibin0506
  • qibin0506
  • 2016-01-22 23:49
  • 6419

go 设计模式

  • 2017-08-11 09:03
  • 138KB
  • 下载

设计模式-适配器模式(Go语言描述)

在上一篇博客设计模式-策略模式(Go语言描述)中我们用最简单的代码用go语言描述了 设计模式中的策略模式,用最简单的实例来描述相信可以让初学者可以很轻松的掌握各种设计模式。继上篇博客,我们接着用同样...
  • qibin0506
  • qibin0506
  • 2016-01-28 08:56
  • 6264

Go 单例模式

单例模式,是一种常用的软件设计模式,在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以包在系统中一个类只有一个实例且该实例易于访问,从而方便对实例个数的控制并节约系统资源。 懒汉...
  • lengyuezuixue
  • lengyuezuixue
  • 2017-12-25 15:46
  • 87

golang 中的 类型判断

golang 中的 类型判断 类似于javascript中typeof 和 java中 instanceof 比如 var a interface{} newA,ok:=a.(string) ...
  • suncaishen
  • suncaishen
  • 2013-04-15 23:51
  • 20559

设计模式-模板方法模式(Go语言描述)

这篇文章我们还是继续我们的设计模式系列, 今天我们带来的一个全新的设计模式在实际开发中大家肯定都遇到过, 可能大家只是不知道它叫模板方法模式而已, 今天我们就来详细的说一下什么是模板方法模式,已经该模...
  • qibin0506
  • qibin0506
  • 2016-06-24 09:05
  • 4915

golang 并发设计模式(二)--管道模式管道和显式取消

摘自点击打开链接 Go Concurrency Patterns: Pipelines and cancellation 一、 引言 Go并发原语使得构建流式数据管道,高效利用I/O和多核变得...
  • hittata
  • hittata
  • 2016-06-29 13:41
  • 1870

设计模式-适配器模式(Go语言描述)

在上一篇博客设计模式-策略模式(Go语言描述)中我们用最简单的代码用go语言描述了 设计模式中的策略模式,用最简单的实例来描述相信可以让初学者可以很轻松的掌握各种设计模式。继上篇博客,我们接着用同样...
  • qibin0506
  • qibin0506
  • 2016-01-28 08:56
  • 6264
    个人资料
    • 访问:645442次
    • 积分:6893
    • 等级:
    • 排名:第3918名
    • 原创:80篇
    • 转载:0篇
    • 译文:2篇
    • 评论:625条
    文章分类
    博客专栏
    友情链接

    鸿洋_

    Aggie的博客

    梁肖技术中心

    极客导航

    最新评论