打印

Go的单例模式

[复制链接]
1149|12
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
keer_zu|  楼主 | 2016-12-1 10:50 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 keer_zu 于 2016-12-1 10:56 编辑

单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。
通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。
如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
1.Go实现非线程安全的单例模式:

package singleton

type singleton struct {
}

var instance *singleton

func GetInstance() *singleton {
        if instance == nil {
                instance = &singleton{}   // <--- NOT THREAD SAFE
        }
        return instance
}
                                                                                                   
非线程安全的单例模式是大家用得最多的一种。在Github上面的开源项目有很多都使用了非线程安全的。
这种写法lazy loading很明显,但是致命的是在多线程不能正常工作。




2.Go实现带线程锁的单例模式:
var mu Sync.Mutex

func GetInstance() *singleton {
    mu.Lock()                    // <--- Unnecessary locking if instance already created
    defer mu.Unlock()

    if instance == nil {
        instance = &singleton{}
    }
    return instance
<font size="4">}</font>
这里用了Go的:
Sync.Mutex

sync/mutex是Go语言底层基础对象之一,用于构建多个goroutine间的同步逻辑,因此被大量高层对象所使用。
其工作模型类似于Linux内核的futex对象,具体实现极为简洁,性能也有保证。

mutex对象仅有两个数值字段,分为为state(存储状态)和sema(用于计算休眠goroutine数量的信号量)。
初始化时填入的0值将mutex设定在未锁定状态,同时保证时间开销最小。
这一特性允许将mutex作为其它对象的子对象使用。






3.带检查锁的的单例模式
func GetInstance() *singleton {
    if instance == nil {     // <-- Not yet perfect. since it's not fully atomic
        mu.Lock()
        defer mu.Unlock()

        if instance == nil {
            instance = &singleton{}
        }
    }
    return instance
}
这是一个不错的方法,但是还并不是很完美。因为编译器优化没有检查实例存储状态。如果使用sync/atomic包的话 就可以自动帮我们加载和设置标记。

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中使用单例设计的是这种
package singleton

import (
    "sync"
)

type singleton struct {
}

var instance *singleton
var once sync.Once

func GetInstance() *singleton {
    once.Do(func() {
        instance = &singleton{}
    })
    return instance
}


通过使用sync.Once 包可以实现线程安全的单例模式。




相关帖子

沙发
keer_zu|  楼主 | 2016-12-1 11:14 | 只看该作者
我代码中的单例模式:

分别对CommandDb和TaskPool(之前的CommandPool)实现了单例模式,保证对象实例的唯一性,使用测试函数做了测试,三个文件代码如下:

command.go:


    package message

    import (
            "errors"
            "fmt"
            "github.com/pojoin/golis"
            "sync"
    )

    var once sync.Once

    type CommandProperty int

    //const (
    //        ProvideResult CommandProperty = iota
    //        NoProvideResult
    //)

    type Command interface {
            BuildResponse(info interface{}) (string, error)
            CommandHandle(session *golis.Iosession) error
            GetCommandName() string
            IsProvidedResult() bool
    }

    type CommandAttribute struct {
            CommandName string
            //MsgId            string
            IsProvidedResult bool //CommandProperty
    }

    type DmcCommandRegist struct {
            cmdAtt    CommandAttribute
            RoomId    int32
            SessionId int32
    }

    func (dcr *DmcCommandRegist) BuildResponse(info interface{}) (string, error) {
            return "abc", errors.New("cde")
    }

    func (dcr *DmcCommandRegist) CommandHandle(session *golis.Iosession) error {
            return errors.New("abc")
    }

    func (dcr *DmcCommandRegist) GetCommandName() string {
            return dcr.cmdAtt.CommandName
    }

    func (dcr *DmcCommandRegist) IsProvidedResult() bool {
            return dcr.cmdAtt.IsProvidedResult
    }

    type CommandDb struct {
            CommandMap map[string]*Command
    }

    var commandDbInstance *CommandDb

    func GetCommandDbInstance() *CommandDb {
            once.Do(func() {
                    fmt.Println("+++++++++++ commandDb instance +++++++")
                    commandDbInstance = &CommandDb{}
            })
            return commandDbInstance
    }

    func (cdb *CommandDb) AddCommand(cmd *Command, cmdName string) {
            cdb.CommandMap[cmdName] = cmd
    }

    func (cdb *CommandDb) GetCommand(name string) (*Command, error) {

            if cdb.CommandMap[name] != nil {
                    return cdb.CommandMap[name], nil
            }

            err := errors.New("This command is not exist!")

            return nil, err
    }





task.go:

    package message

    import (
            "fmt"
            //        "errors"
            //        "github.com/pojoin/golis"
            "sync"
    )

    var taskPoolOnce sync.Once

    type TaskInfo struct {
            Cmd   *Command
            MsgId string
    }

    type TaskPool struct {
            WaitMap map[string]*TaskInfo
    }

    var taskPoolInstance *TaskPool

    func GetTaskPoolInstance() *TaskPool {
            taskPoolOnce.Do(func() {
                    fmt.Println("+++++++++++ taskPool instance +++++++")
                    taskPoolInstance = &TaskPool{}
            })
            return taskPoolInstance
    }

    func (tp *TaskPool) Add(task *TaskInfo, msgId string) {
            tp.WaitMap[msgId] = task
    }

    func (tp *TaskPool) Del(msgId string) {
            delete(tp.WaitMap, msgId)
    }



测试函数:

    package main

    import (
            "fmt"
            "message"
    )

    func main() {
            fmt.Println("message test")
            var cmddb1, cmddb2, cmddb3 *message.CommandDb
            cmddb1 = message.GetCommandDbInstance()
            s1 := fmt.Sprintf(" %d", cmddb1)
            fmt.Println("cmddb1: ", s1)
            cmddb2 = message.GetCommandDbInstance()
            s2 := fmt.Sprintf(" %d", cmddb2)
            fmt.Println("cmddb2: ", s2)
            cmddb3 = message.GetCommandDbInstance()
            s3 := fmt.Sprintf(" %d", cmddb3)
            fmt.Println("cmddb3: ", s3)

            var taskp1, taskp2, taskp3 *message.TaskPool
            taskp1 = message.GetTaskPoolInstance()
            t1 := fmt.Sprintf(" %d", taskp1)
            fmt.Println("taskp1: ", t1)
            taskp2 = message.GetTaskPoolInstance()
            t2 := fmt.Sprintf(" %d", taskp2)
            fmt.Println("taskp2: ", t2)
            taskp3 = message.GetTaskPoolInstance()
            t3 := fmt.Sprintf(" %d", taskp3)
            fmt.Println("taskp3: ", t3)
    }




@yyy71cj @21ic小喇叭 @dong_abc @ayb_ice @Simon21ic

使用特权

评论回复
板凳
keer_zu|  楼主 | 2016-12-1 12:54 | 只看该作者
yyy71cj 发表于 2016-12-1 12:27
这语法,兼顾了C和PASCAL哇……

哪一点是pascal的?没用过pascal。

使用特权

评论回复
地板
keer_zu|  楼主 | 2016-12-1 13:26 | 只看该作者
yyy71cj 发表于 2016-12-1 13:04
:= 这是PASCAL的赋值符

这是赋值+声明

使用特权

评论回复
5
keer_zu|  楼主 | 2016-12-1 16:13 | 只看该作者
yyy71cj 发表于 2016-12-1 14:13
所以说嘛,各有那么yi点踪影,又不完全相同

这些都是表面的

使用特权

评论回复
6
icecut| | 2016-12-1 23:43 | 只看该作者
我也有go的培训视频

使用特权

评论回复
7
Simon21ic| | 2016-12-1 23:56 | 只看该作者
go的用户数量确实越来越多,代码看上去比较像swift
和go相比,我更加喜欢swift,原因很简单,swift更加接近自然语言,并且,我自己的iOS APP也是用swift。当然,我的一些tweak项目还是用Objective-C。

其实,本来想写个帖子介绍swift,不过我也只做过1个上线的iOS应用,还只是刚刚入门,就放弃了。何况,我做的也只是项目一开始的测试评估用的APP,以后项目启动的话,也是找专业的人开发。

使用特权

评论回复
8
keer_zu|  楼主 | 2016-12-2 08:29 | 只看该作者
Simon21ic 发表于 2016-12-1 23:56
go的用户数量确实越来越多,代码看上去比较像swift
和go相比,我更加喜欢swift,原因很简单,swift更加接近 ...

期待开swift专题

使用特权

评论回复
9
m564522634| | 2016-12-2 10:41 | 只看该作者
请教个菜鸟问题,go在效率上能不能和C语言相比

使用特权

评论回复
10
keer_zu|  楼主 | 2016-12-2 11:05 | 只看该作者
m564522634 发表于 2016-12-2 10:41
请教个菜鸟问题,go在效率上能不能和C语言相比

http://news.chinaunix.net/opensource/2013/0515/2764818.shtml

效率是没问题的,特别是在PC上

使用特权

评论回复
11
Simon21ic| | 2016-12-3 19:45 | 只看该作者

呵呵,不知道有没有人有兴趣
swift类似go,也只是一种针对性很强的语言,基本只针对MAC或者iOS的开发
go基本是针对并行网络应用的开发

不过,我开始用swift的时候,已经是3.0了,根据apple的改动,看得出apple想要的方向,只是目前觉得swift3.0还差不少

使用特权

评论回复
12
keer_zu|  楼主 | 2016-12-5 08:22 | 只看该作者
Simon21ic 发表于 2016-12-3 19:45
呵呵,不知道有没有人有兴趣
swift类似go,也只是一种针对性很强的语言,基本只针对MAC或者iOS的开发
go ...

苹果难道要拿它来替代object-c?

使用特权

评论回复
13
keer_zu|  楼主 | 2016-12-28 23:04 | 只看该作者
继续。。。。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:qq群:49734243 Email:zukeqiang@gmail.com

1349

主题

12426

帖子

53

粉丝