go数据同步(sync与atomic包)

目录

原子操作atomic

互斥锁

读写互斥锁

组等待

条件等待

单次执行

临时对象池

安全map


Golang推荐通过channel 进行通信和同步,但在实际开发中sync包用的也较多;另外sync下还有一个atomic包,提供了一些底层的原子操作。

 

原子操作atomic

atomic包(sync/atomic)提供了底层的原子级内存操作。

共有五种操作:增减, 比较并交换, 载入, 存储,交换(T代表int32、int64、uint32、uint64、unitptr、pointer(没有增减操作))

  • func LoadT(addr *T) (val T) :读取;

  • func StoreT(addr *T, val T):写入;

  • func AddT(addr *T, delta T) (new T):增减,返回新的值;

  • func SwapT(addr *T, new T) (old T):交换,并返回旧值;

  • func CompareAndSwapT(addr *T, old, new T) (swapped bool):比较addr中保存的值是否与old相等,若相等则替换为新值,并返回true;否则,直接返回false。

示例程序

var sum uint32 = 100
atomic.CompareAndSwapUint32(&sum, 100, sum+1) // 101
atomic.CompareAndSwapUint32(&sum, 100, sum+1) // 101,不等没有变化
atomic.AddUint32(&sum, 1) // 102

 

互斥锁

互斥锁用来保证在任一时刻,只能有一个例程访问某对象。Mutex 的初始值为解锁状态。Mutex 通常作为其它结构体的匿名字段使用,使该结构体具有 Lock 和 Unlock 方法。

相关函数

  • func (m *Mutex) Lock():锁住 m,如果 m 已经被加锁,则 Lock 将被阻塞;

  • func (m *Mutex) Unlock():解锁 m,如果 m 未加锁,则该操作会引发 panic;

锁相关主要操作接口

type Locker interface {
    Lock()
    Unlock()
}

 

读写互斥锁

RWMutex 比 Mutex 多了一个“读锁定”和“读解锁”,可以让多个例程同时读取某对象。RWMutex 的初始值为解锁状态。RWMutex 通常作为其它结构体的匿名字段使用。

相关函数

  • func (rw *RWMutex) Lock():将 rw 设置为写锁定状态,禁止其他例程读取或写入;

  • func (rw *RWMutex) Unlock():解除 rw 的写锁定状态,如果 rw 未被写锁定,则该操作会引发 panic;

  • func (rw *RWMutex) RLock():将 rw 设置为读锁定状态,禁止其他例程写入,但可以读取;

  • func (rw *RWMutex) RUnlock():解除 rw 的读锁定状态,如果 rw 未被读锁顶,则该操作会引发 panic;

  • func (rw *RWMutex) RLocker() Locker:返回一个互斥锁,将 rw.RLock 和 rw.RUnlock 封装成了一个 Locker 接口;

 

组等待

WaitGroup 用于等待一组例程的结束。主例程在创建每个子例程的时候先调用 Add 增加等待计数,每个子例程在结束时调用 Done 减少例程计数。之后,主例程通过 Wait 方法开始等待,直到计数器归零才继续执行。

相关函数

  • func (wg *WaitGroup) Add(delta int):计数器增加 delta,delta 可以是负数;

  • func (wg *WaitGroup) Done():计数器减少 1;

  • func (wg *WaitGroup) Wait():等待直到计数器归零。如果计数器小于 0,则该操作会引发 panic。

示例程序

wg := sync.WaitGroup{}
for i := 0; i < 10; i++ {
	wg.Add(1)  // 进入例程前调用,也可在循环外直接加10
	go func(i int) {
		defer wg.Done()
		fmt.Print(i, " ")
	}(i)
}
wg.Wait()

 

条件等待

Cond条件变量是线程间共享的一种机制,主要包括两个动作:

  • 一个/或多个线程等待"条件变量的条件成立"而挂起:wait,在 Wait 之前应当手动为 c.L 上锁,Wait 结束后手动解锁。为避免虚假唤醒,需要将 Wait 放到一个条件判断循环中。

  • 另一个线程使"条件成立"(给出条件成立信号):

    • Signal:唤醒一个等待线程;

    • Broadcast:唤醒所有等待线程

  • 为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。

相关函数

  • func NewCond(l Locker) *Cond:创建一个条件等待

  • func (c *Cond) Broadcast():唤醒所有等待者

  • func (c *Cond) Signal():唤醒一个等待者

  • func (c *Cond) Wait():解锁 c.L 并进入等待状态,在被唤醒时会自动重新锁定 c.L。

示例:条件等待

condition := false // 条件
var mu sync.Mutex
cond := sync.NewCond(&mu)

// 唤醒者
go func() {
	time.Sleep(...)
	mu.Lock()
	condition = true // 更改条件
	cond.Signal()    // 发送通知:条件已经满足
	mu.Unlock()
}()

// 等待者
go func() {
	mu.Lock()
	// 检查条件是否满足,避免虚假通知,同时避免 Signal 提前于 Wait 执行。
	for !condition {
		cond.Wait() // 等待时 mu 处于解锁状态,唤醒时重新锁定。
	}
	// ...
	mu.Unlock()
}

 

单次执行

Once 的作用是多次调用但只执行一次。Once 只有一个方法Do(),向 Do 传入一个函数,这个函数在第一次执行 Once.Do() 的时候会被调用,以后再执行 Once.Do() 将没有任何动作,即使传入了其它的函数,也不会被执行,如果要执行其它函数,需要重新创建一个 Once 对象。

Once 可以安全的在多个例程中并行使用。

相关函数

  • func (o *Once) Do(f func())

 

临时对象池

sync.Pool对象就是一组临时对象的集合。用于存储那些被分配了但是没有被使用,而未来可能会使用的值(放进Pool中的对象,会随时被回收掉;所以如果事先Put进去100个对象,下次Get的时候发现Pool是空也是有可能的),以减小垃圾回收的压力。

Pool是协程安全的。相关函数

  • func (p *Pool) Get() interface{} :返回Pool中的任意一个对象;

    • 如果Pool为空,则调用New返回一个新创建的对象;

    • 如果没有设置New,则返回nil;

  • func (p *Pool) Put(x interface{}):放入Pool;

  • New func() interface{}:设定New方法;

 

示例程序

var bytePool = sync.Pool{
  New: func() interface{} {
    b := make([]byte, 1024)
    return &b
  },
}
for i := 0; i < 10000; i++{
  obj := bytePool.Get().(*[]byte)
  _ = obj
  // ...
  bytePool.Put(obj)
}

 

安全map

为解决map并发读写问题,引入了sync.map。sync.map针对相对稳定的key(读多、写少情况)做了优化,若是频繁的读写则使用内置map与RWMutex更好。

主要函数

  • func (m *Map) Delete(key interface{})

  • func (m *Map) Load(key interface{}) (value interface{}, ok bool)

  • func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool):如果Key存在,则返回对应的值;否则添加value,并返回

  • func (m *Map) Range(f func(key, value interface{}) bool):枚举map元素并执行func,若返回false,则停止枚举

  • func (m *Map) Store(key, value interface{})

示例程序

var m sync.Map
vv, ok := m.LoadOrStore("1", "one")
fmt.Println(vv, ok) //one false
vv, ok = m.Load("1")
fmt.Println(vv, ok) //one true
vv, ok = m.LoadOrStore("1", "oneone")
fmt.Println(vv, ok) //one true
m.Store("1", "oneone")
vv, ok = m.Load("1")
fmt.Println(vv, ok) // oneone true
m.Store("2", "two")
m.Range(func(k, v interface{}) bool {
	fmt.Println(k, v)
	return true
})
m.Delete("1")

 

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值