在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
最近在学习Golang,想着可以就以前的知识做一些串通,加上了解到go语言也是面向对象编程语言之后。在最近的开发过程中,我碰到一个问题,要用go语言实现单例模式。本着“天下知识,同根同源”(我瞎掰的~),我心想,这有什么难的,可是真正做起来,还是碰到了不少问题。 下面是我的经历: 1.我先是完成了我的第一版单例模式,就是非并发,最简单的一种,懒汉模式: var instance *single type single struct{ Name string } func GetInstance()*single{ if m == nil{ m = &single{} } return m } func main(){ a := GetInstance() a.Name = "a" b := GetInstance() b.Name = "b" fmt.Println(&a.Name, a) fmt.Println(&b.Name, b) fmt.Printf("%p %T\n", a, a) fmt.Printf("%p %T\n", b, b) } 结果如下: 0xc04203e1b0 &{b} 可以看到,我们已经实现了简单的单例模式,我们申请了两次实例,在改变一个第二个实例的字段之后,第一个也随之改变了。而且从他们的地址都相同也可以看出是同一个对象。但是,这样简陋的单例模式在并发下就容易出错,非线程安全的。 现在我们是在并发的情况下去调用的 2.紧接着我们做了一些改进,给单例模式加了锁: var m *single var lock sync.Mutex type single struct{ Name string } func GetInstance()*single{ lock.Lock() defer lock.Unlock() if m == nil{ m = &single{} } return m } 结果同上。 与此同时,新的问题出现了,在高并发环境下,现在不管什么情况下都会上一把锁,而且加锁的代价是很大的,有没有办法继续对我们的代码进行进一步的优化呢? 3.双重锁机制: var m *single var lock sync.Mutex type single struct{ Name string } func GetInstance()*single{ if m == nil{ lock.Lock() defer lock.Unlock() if m == nil{ m = &single{} } } return m } 这次我们用了两个判断,而且我们将同步锁放在了条件判断之后,这样做就避免了每次调用都加锁,提高了代码的执行效率。理论上写到这里已经是很完美的单例模式了,但是我们在go语言里,我们有一个很优雅的写法。 4.sync包里的Once.Do()方法 var m *single var once sync.Once type single struct{ Name string } func GetInstance()*single{ once.Do(func() { m = &single{} }) return m } Once.Do方法的参数是一个函数,这里我们给的是一个匿名函数,在这个函数中我们做的工作很简单,就是去赋值m变量,而且go能保证这个函数中的代码仅仅执行一次!
以后在用go语言写单例模式的时候,可不要再傻傻的去使用前面那些例子了,既然已经有了优雅又强大的方法,我们直接用就完了。 |
请发表评论