前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go语言高并发,单例,保姆级教程!

Go语言高并发,单例,保姆级教程!

作者头像
小锟哥哥
发布2022-05-10 09:12:28
3030
发布2022-05-10 09:12:28
举报
文章被收录于专栏:GoLang全栈

在开发中,某些工具类我们只希望他在程序中被创建一次,每次使用时都不会再创建实例。

这个需求我们就可以使用到单例模式来处理。

一、单例模式能解决什么问题?

保证一个类只有一个实例

其实 go 语言中,没有类这个概念,被抽象成了结构体。

但是为了方便大家理解,我们依旧还是用类来描述,望大家能理解,别在这个问题上纠结。

他的运作方式是什么样的呢?

如果你创建了一个对象,过了一会儿再想创建新对象时,此时你获取到的是你之前创建的对象,而不是新的对象。

主要用处:

我们常用他来提供全局工具,通知中心,全局配置等地方。

二、实现方式

我们大致分为以下三步:

  • 1、创建一个私有变量用于保存单例类型。 go 语言里面只要是小写字幕开头都属于是私有变量,包外就不能被调用。
  • 2、我们需要对外提供一个构建单例的获取方法。 我们一般会以 Instance 来结尾或者开头。
  • 3、在公有方法里面去判定私有变量是否被初始化,并返回。
代码语言:javascript
复制
package main

import "fmt"

type AbsentStruct struct {
 Name  string
}

// 第一步
var Absent *AbsentStruct

// 第二步
func AbsentInstance() *AbsentStruct {
  // 第三步
 // 如果为nil,代表未初始化,初始化他
 if Absent==nil {
  Absent = new(AbsentStruct)
 }
 return Absent
}

func main() {
 m1 := AbsentInstance()
 m1.Name = "张三"
 m2 := AbsentInstance()
 m2.Name = "李四"
 fmt.Printf("%p,%p \n %s,%s \n",m1,m2,m1.Name,m2.Name)
}

以下是运行结果:

代码语言:javascript
复制
$ go run instance/main.go
0xc000010230,0xc000010230
 李四,李四

你会发现 m1 和 m2 的地址是一样的,同时 m2 修改 Name 后 m1 也同样被修改了。这就是一个最简单的单例代码。

三、并发情况下的问题

如果在生产中我们使用上面的方式去创建单例,在第三步时,高并发的情况会出现重复创建。

我们改下我们的 main 方法:

代码语言:javascript
复制
func main() {
  wg := sync.WaitGroup{}
  for i := 0; i < 1000; i++ {
    go func() {
      m1 := AbsentInstance()
      fmt.Printf("%p \n %s \n",m1,m1.Name)
    }()
  }
  wg.Wait()
}

此时再运行:

代码语言:javascript
复制
$ go run instance/main.go
0xc000100000

0xc000100000

0xc000092000

0xc000092000

0xc000092000

我这里只截取了前面几个,你看前面两个和第三个就出现了不一样,后面的数据就基本不会出现异常了,因为私有变量已经被赋值了。

所以我们一般还会在第三步时给他加上锁,保证同一时间只执行一次。

代码语言:javascript
复制
// 第二步
func AbsentInstance() *AbsentStruct {
  // 第三步
 // 如果为nil,代表未初始化,初始化他
 if Absent==nil {
    //上锁
  lock.Lock()
  defer lock.Unlock()
    if singleInstance == nil {
      Absent = new(AbsentStruct)
    }
 }
 return Absent
}

这样就能保证我们在创建实例时,只会有一个线程在跑。

三、更便捷的方法

这样去写单例,代码其实有点多,我们可以使用官方的 sync 包里面的

代码语言:javascript
复制
var once = sync.Once{}
func AbsentInstance() *AbsentStruct {
 once.Do(func() {
  absent = new(AbsentStruct)
 })
 return absent
}

是不是一下就清爽了!

你学废了么?

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-04-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 GoLang全栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、单例模式能解决什么问题?
  • 二、实现方式
  • 三、并发情况下的问题
  • 三、更便捷的方法
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档