sync包 | 青训营笔记

96 阅读18分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天

前言

大家好呀,这是我参加青训营伴学笔记创作活动的第 5 天,如存在问题,烦请各位斧正!

sync包

概述

包括如下内容:

Mutex: 互斥锁 |

RWMutex:读写锁 |

WaitGroup:并发等待组 |

Once:执行一次 |

Cond:信号量 |

Pool:临时对象池 |

Map:自带锁的map |

Mutex基本使用

1)基本用法:

var lock sync.Mutex  或 lock := &sync.Mutex{} ,下面如果没有特殊强调,都可以这样创建实例

(1)阻塞式加锁:lock.Lock()(当锁被占有,其他goroutine尝试获取锁时会被阻塞)

(2)非阻塞式加锁:lock.TryLock()(返回true或false,1.18以后新增)

(3)解锁:lock.UnLock()

2)还可以将锁声明在结构体中,然后通过此结构体的实例进行加锁解锁:

type Bank struct {  
     sync.Mutex
     balance map[string]float64
}

RWMutex基本使用

1)对读操作的锁定:func (rw *RWMutex) RLock()

2)对读操作的解锁:func (rw *RWMutex) RUnlock()

3)对写操作的锁定:func (rw *RWMutex) Lock()

4)对写操作的解锁:func (rw *RWMutex) Unlock()

5)能获取读锁,然后传递给其他协程使用:func (rw *RWMutex) RLocker() Locker

WaitGroup基本使用

1)作用:等待一组工作完成后,再进行下一组工作。

2)基本用法:

func (wg *WaitGroup) Add(delta int) // 计数器+delta

func (wg *WaitGroup) Done() // 计数器-1

func (wg *WaitGroup) Wait() // 阻塞直到计数器为0

2)它没有办法指定最大并发协程数,比如无法限制某一些时刻出现大量连接数据库导致数据库不可访问。

所以,为了能够控制最大的并发数,推荐使用github.com/remeh/sizedwaitgroup,用法和sync.WaitGroup非常类似。

Once基本使用

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

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

2)sync.Once的结构如下所示,只有一个函数。

使用变量done来记录函数的执行状态,使用sync.Mutex和sync.atomic来保证线程安全的读取done。

3)基本使用:声明完once后,once.do(匿名函数)

Cond基本使用

1)Cond指的是同步条件变量,一般需要与互斥锁组合使用,本质上是一些正在等待某个条件的协程的同步机制。

2)创建实例,需要传入一个互斥锁:

func NewCond(l Locker) *Cond {    
    return &Cond{
        L: l
    }         
}  

通过.L可以访问到它内部的互斥锁。

3)基本使用,三个函数:Wait()Signal()Broadcast() ,后面两个是单个唤醒和全部唤醒,最好使用全部唤醒。

一般使用for循环判断是否满足条件 不满足继续wait。

Pool基本使用

1)背景:Gc在带来了编程的方便但同时也增加了运行时开销,使用不当可能会严重影响程序的性能,因此性能要求高的场景不能任意产生太多的垃圾。

sync.Pool正是用来解决这类问题的,Pool可以作为临时对象池来使用,不再自己单独创建对象,而是从临时对象池中获取出一个对象。

2)原理:sync.Pool在init的时候注册了一个poolCleanup函数,它会清除所有的pool里面的所有缓存的对象,

该函数注册进去之后会在每次Gc之前都会调用,因此sync.Pool缓存的期限只是两次Gc之间这段时间。

2)基本使用:sync.Pool有2个函数Get和Put,Get负责从临时对象池中取出一个对象,Put用于结束的时候把对象放回临时对象池中。

func (p *Pool) Get() interface{}

func (p *Pool) Put(x interface{})

3)使用案例:

b := bufPool.Get().(*bytes.Buffer)    // 获取临时对象,没有的话会自动创建
b.Reset()    // 操作b,多个方法
bufPool.Put(b)    // 将临时对象放回到 Pool 中

Map基本使用

1)背景:1.9版本之前自带的map对象是不具有并发安全的,多数时候需要我们自己实现map并发安全,比如给map加读写锁。

2)sync.Map封装了更为复杂的数据结构实现了比之前加读写锁锁map更优秀的性能。

3)总共5个函数:

func (m *Map) Load(key interface{}) (value interface{}, ok bool)    
// 查询一个key
func (m *Map) Store(key, value interface{})    
// 设置key value
func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)  
// 如果key存在则返回对应的value,否则设置key value
func (m *Map) Delete(key interface{})    
// 删除一个key
func (m *Map) Range(f func(key, value interface{}) bool)    
// 遍历map,仍然是无序的