首页 > 其他分享 >(转)golang常用库之-标准库 sync包| go语言如何实现单例模式、读写锁(sync.RWMutex)

(转)golang常用库之-标准库 sync包| go语言如何实现单例模式、读写锁(sync.RWMutex)

时间:2023-02-06 09:11:29浏览次数:41  
标签:Do 单例 sync golang func Once once

原文:https://blog.csdn.net/inthat/article/details/124218961

golang常用库之-标准库 sync包
Golang sync包提供了基础的异步操作方法,包括互斥锁Mutex,执行一次Once和并发等待组WaitGroup。

Mutex: 互斥锁
RWMutex:读写锁
WaitGroup:并发等待组
Once:执行一次
Cond:信号量
Pool:临时对象池
Map:自带锁的map

go语言如何实现单例模式
关于单例模式(Singleton Pattern)
设计模式(Design pattern),提供了在软件开发过程中面临的一些问题的最佳解决方案。
主要分创建型模式、结构型模式和行为型模式。

首先来看创建型模式(Creational Patterns),它提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。

单例模式属于创建型模式,单例模式,顾名思义就是只有一个实例,并且她自己负责创建自己的对象,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

单例模式又分为饿汉方式和懒汉方式。

饿汉式,从名字上也很好理解,就是“比较勤”,实例在初始化的时候就已经建好了,不管你有没有用到,都先建好了再说。好处是没有线程安全的问题,坏处是浪费内存空间。
懒汉式,顾名思义就是实例在用到的时候才去创建,“比较懒”,用的时候才去检查有没有实例,如果有则返回,没有则新建。
对于golang,饿汉方式指全局的单例实例在包被加载时创建,而懒汉方式指全局的单例实例在第一次被使用时创建。

除了自己手写饿汉方式和懒汉方式,在 Go 开发中,还有一种更优雅的实现方式(使用sync包的once.Do)

sync.Once
sync.Once指的是只执行一次的对象实现,常用来控制某些函数只能被调用一次。sync.Once的使用场景例如单例模式、系统初始化。

例如并发情况下多次调用channel的close会导致panic,解决这个问题我们可以使用sync.Once来保证close只会被执行一次。

sync.Once的结构如下所示,只有一个函数。使用变量done来记录函数的执行状态,使用sync.Mutex和sync.atomic来保证线程安全的读取done。

type Once struct {
m Mutex #互斥锁
done uint32 #执行状态
}

func (o *Once) Do(f func())
1
2
3
4
5
6
举个例子,1000个并发协程情况下只有一个协程会执行到fmt.Printf,多次执行的情况下输出的内容还不一样,因为这取决于哪个协程先调用到该匿名函数。

func main() {
once := &sync.Once{}

for i := 0; i < 1000; i++ {
go func(idx int) {
once.Do(func() {
time.Sleep(1 * time.Second)
fmt.Printf("hello world index: %d", idx)
})
}(i)
}

time.Sleep(5 * time.Second)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
demo:使用sync包的once.Do,实现单例模式
package singleton

import (
"sync"
)

type singleton struct {
}

var ins *singleton
var once sync.Once

func GetInsOr() *singleton {
once.Do(func() {
ins = &singleton{}
})
return ins
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
使用once.Do可以确保 ins 实例全局只被创建一次,once.Do 函数还可以确保当同时有多个创建动作时,只有一个创建动作在被执行。

package singleton

import (
"unsafe"
"fmt"
"sync"
"testing"
)

//协程安全 单例模式
type Singleton struct {
}

var singleintance *Singleton
var once sync.Once

//只执行一次
func GetSingletonObj() *Singleton {
once.Do(func() {
fmt.Println("Create Obj")
singleintance = new(Singleton)
})
return singleintance
}

func TestGetSingletonObj(t *testing.T) {
var wg sync.WaitGroup //协程安全
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
obj := GetSingletonObj()
fmt.Printf("%x\n",unsafe.Pointer(obj))//输出的结果都是同一个地址
wg.Done()
}()
}
wg.Wait() //等待是有协程运行完成
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
使用noce.Do调用时方法内执行代码只执行一次。

输出结果:

Create Obj
6c7df8
6c7df8
6c7df8
6c7df8
6c7df8
6c7df8
6c7df8
6c7df8

读写锁(sync.RWMutex)
在读多写少的环境中,可以优先使用读写互斥锁(sync.RWMutex),它比互斥锁更加高效。sync 包中的 RWMutex 提供了读写互斥锁的封装。

读写锁分为:读锁和写锁

如果设置了一个写锁,那么其它读的线程以及写的线程都拿不到锁,这个时候,与互斥锁的功能相同
如果设置了一个读锁,那么其它写的线程是拿不到锁的,但是其它读的线程是可以拿到锁
通过设置写锁,同样可以实现数据的一致性:

package main
import ("fmt"
"sync"
)

var (
count int
rwLock sync.RWMutex
)

func main() {
for i := 0; i < 2; i++ {
go func() {
for i := 1000000; i > 0; i-- {
rwLock.Lock()
count ++
rwLock.Unlock()
}
fmt.Println(count)
}()
}

fmt.Scanf("\n") //等待子线程全部结束
}

运行结果:
1968637
2000000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

————————————————

标签:Do,单例,sync,golang,func,Once,once
From: https://www.cnblogs.com/liujiacai/p/17094392.html

相关文章

  • (转)深入浅出Golang Runtime
    原文:https://www.cnblogs.com/lovezbs/p/14467801.html本文为腾讯NOW直播研发工程师郝以奋在8月19日深圳GopherMeetup上的分享,以下为根据PPT进行的详细注解。介绍......
  • (转)Golang标准库——runtime
    原文:https://www.jianshu.com/p/c1b6de70c004runtimeruntime包提供和go运行时环境的互操作,如控制go程的函数。它也包括用于reflect包的低层次类型信息;参见》reflect报......
  • 异步编程(协程asyncio)
    协程1.异步非阻塞,asyncio2.异步框架:提升性能tomadofastapidjango3.xasgiaiohttp协程是什么协程是不是计算机提供出来的,程序员自己创建的。​协程(corout......
  • 单例模式-go语言实现
    一、理论知识单例设计模式(SingletonDesignPattern)是指一个对象只允许被实例化一次,并提供一个访问该实例的全局访问点。应用场景:避免资源访问冲突,例如写日志文件操作;......
  • golang变量
    1.golang变量命名规则由26个英文字母大小写,0-9,_组成。变量名不能以数字开头。变量都是先声明再使用,一旦声明必须使用。2.golang变量赋值方法2.1单个变量赋值......
  • Spring异步Async和事务Transactional注解
    Spring开发中我们我们常常用到@Transaction和@Async,但这2个注解加在一起很多的开发者不敢用,担心事务不生效。下面我们就仔细讲解一下这2个注解同时运用,文章用3个场景讲述它......
  • (转)Golang 中关于闭包的坑
    原文:https://www.jianshu.com/p/fa21e6fada70所谓闭包是指内层函数引用了外层函数中的变量或称为引用了自由变量的函数,其返回值也是一个函数,了解过的语言中有闭包概念的......
  • (转)[Journey with golang] 5. Concurrent
    原文:https://www.cnblogs.com/JHSeng/p/12214964.html并发和并行是两种不同的概念。并行意味着程序在任意时刻都是同时运行的并发意味着程序在单位时间内是同时运行的......
  • (转)[Journey with golang] 1. Basic knowledge
    原文:https://www.cnblogs.com/JHSeng/p/12128461.html这一章介绍golang的一些基础知识。golang写起来跟c++很不一样,在我看来,它更像是c++/java/python的混合体,再经过一系列......
  • 【Spring事物三千问】TransactionSynchronizationManager的原理分析
    TransactionSynchronizationManagerTransactionSynchronizationManager是管理每个线程的DB连接资源和事务同步的核心委托类。如果事务同步未处于活动状态,则表示当前没有......