标签:class 关于 调用 get 地址 obj bug 这一 标准库
原文地址:http://marcio.io/2015/07/singleton-pattern-in-go/
最近,我在很多Github库里看到这种类型的错误,单例模式的实现没有考虑线程安全,下面是常识性错误的代码
package singleton type singleton struct { } var instance *singleton func GetInstance() *singleton { if instance == nil { instance = &singleton{} // <---非线程安全的 } return instance }
我也看到一些使用糟糕的方法来解决线程安全的问题。事实上他解决了多线程的问题,但是创造了其他潜在的更严重的问题,他通过对整个方法执行锁定来引入线程竞争
var mu Sync.Mutex func GetInstance() *singleton { mu.Lock() // <--- 如果实例已经被创建就没有必要锁写 defer mu.Unlock() if instance == nil { instance = &singleton{} } return instance }
在c++和其他语言,用于保证最小锁定并且保证线程安全的最好、最安全的方式是当需要锁定时使用众所周知的Check-Lock-Check模式。下面的伪代码说明了这个模式的大概样子
if check() { lock() { if check() { // perform your lock-safe code here } } }
func GetInstance() *singleton { if instance == nil { // <-- 不够完善. 他并不是完全的原子性 mu.Lock() defer mu.Unlock() if instance == nil { instance = &singleton{} } } return instance }
import "sync" import "sync/atomic" var initialized uint32 ... func GetInstance() *singleton { if atomic.LoadUInt32(&initialized) == 1 { return instance } mu.Lock() defer mu.Unlock() if initialized == 0 { instance = &singleton{} atomic.StoreUint32(&initialized, 1) } return instance }
但是.....我相信我们可以通过查看Go语言和标准库的源码看一下go routines 同步的实现方式来做的更好
我们想要使用Go的惯用手法来实现这个单例模式。所以我们需要看一下打包好的sync标准库。我们找到了 Once 类型。这个对象可以精确的只执行一次操作,下面就是Go标准库的代码
// Once is an object that will perform exactly one action. type Once struct { m Mutex done uint32 } // Do calls the function f if and only if Do is being called for the // first time for this instance of Once. In other words, given // var once Once // if once.Do(f) is called multiple times, only the first call will invoke f, // even if f has a different value in each invocation. A new instance of // Once is required for each function to execute. // // Do is intended for initialization that must be run exactly once. Since f // is niladic, it may be necessary to use a function literal to capture the // arguments to a function to be invoked by Do: // config.once.Do(func() { config.init(filename) }) // // Because no call to Do returns until the one call to f returns, if f causes // Do to be called, it will deadlock. // // If f panics, Do considers it to have returned; future calls of Do return // without calling f. // func (o *Once) Do(f func()) { if atomic.LoadUint32(&o.done) == 1 { // <-- Check return } // Slow-path. o.m.Lock() // <-- Lock defer o.m.Unlock() if o.done == 0 { // <-- Check defer atomic.StoreUint32(&o.done, 1) f() } }
这意味着我们可以运用非常棒的 Go sync包来调用一个只执行一次的方法。因此,我们可以向下面这样调用 once.Do() 方法
once.Do(func() { // 执行安全的初始化操作 })
下面你可以看到使用sync.Once类型实现的单例实现的完整代码,用于同步访问GetInstance() 并保证我们的类型初始化只执行一次。
package singleton import ( "sync" ) type singleton struct { } var instance *singleton var once sync.Once func GetInstance() *singleton { once.Do(func() { instance = &singleton{} }) return instance }
标签:class 关于 调用 get 地址 obj bug 这一 标准库
原文地址:http://www.cnblogs.com/li-peng/p/7700184.html