Go语言学习笔记part7

并发2

互斥锁

Go语言中使用sync包的Mutex类型来实现互斥锁

  1. var x int64
  2. var wg sync.WaitGroup
  3. var lock sync.Mutex
  4. func add() {
  5. for i := 0; i < 5000; i++ {
  6. lock.Lock() // 加锁
  7. x = x + 1
  8. lock.Unlock() // 解锁
  9. }
  10. wg.Done()
  11. }
  12. func main() {
  13. wg.Add(2)
  14. go add()
  15. go add()
  16. wg.Wait()
  17. fmt.Println(x)
  18. }

总结一下,Go语言中的互斥锁在sync包中,操作如下:

  1. import "sync"
  2. var lock sync.Mutex
  3. ...
  4. lock.Lock()
  5. //临界区资源操作
  6. lock.Unlock()
  7. ...

读写锁

  1. import "sync"
  2. var rwlock sync.RWMutex
  3. ...
  4. rwlock.Lock()
  5. //写操作
  6. lock.Unlock()
  7. ...
  8. rwlock.RLock()
  9. //读操作
  10. rwlock.RUnlock()
  11. ...

sync.WaitGroup

Go语言中可以使用sync.WaitGroup来实现并发任务的同步。

方法名 功能
(wg * WaitGroup) Add(delta int) 计数器+delta
(wg *WaitGroup) Done() 计数器-1
(wg *WaitGroup) Wait() 阻塞直到计数器变为0

sync.Once

在编程的很多场景下我们需要确保某些操作在高并发的场景下只执行一次,例如只加载一次配置文件、只关闭一次通道等。

Go语言中的sync包中提供了一个针对只执行一次场景的解决方案–sync.Once

sync.Once只有一个Do方法,其签名如下:

  1. func (o *Once) Do(f func()) {}

备注:如果要执行的函数f需要传递参数就需要搭配闭包来使用。

  1. import "sync"
  2. var doOnce sync.Once
  3. doOnce.Do()

sync.Map

Go语言中内置的map不是并发安全的。Go语言的sync包中提供了一个开箱即用的并发安全版map—sync.Map。开箱即用表示不用像内置的map一样使用make函数初始化就能直接使用。同时sync.Map内置了诸如StoreLoadLoadOrStoreDeleteRange等操作方法。

  1. var m = sync.Map{}
  2. func main() {
  3. wg := sync.WaitGroup{}
  4. for i := 0; i < 20; i++ {
  5. wg.Add(1)
  6. go func(n int) {
  7. key := strconv.Itoa(n)
  8. m.Store(key, n)
  9. value, _ := m.Load(key)
  10. fmt.Printf("k=:%v,v:=%v\n", key, value)
  11. wg.Done()
  12. }(i)
  13. }
  14. wg.Wait()
  15. }

原子操作

atomic包

代码中的加锁操作因为涉及内核态的上下文切换会比较耗时、代价比较高。针对基本数据类型我们还可以使用原子操作来保证并发安全,因为原子操作是Go语言提供的方法它在用户态就可以完成,因此性能比加锁操作更好。Go语言中原子操作由内置的标准库sync/atomic提供。

方法 解释
func LoadInt32(addr _int32) (val int32)
func LoadInt64(addr _int64) (val int64)
func LoadUint32(addr _uint32) (val uint32)
func LoadUint64(addr _uint64) (val uint64)
func LoadUintptr(addr _uintptr) (val uintptr)
func LoadPointer(addr _unsafe.Pointer) (val unsafe.Pointer)
读取操作
func StoreInt32(addr _int32, val int32)
func StoreInt64(addr _int64, val int64)
func StoreUint32(addr _uint32, val uint32)
func StoreUint64(addr _uint64, val uint64)
func StoreUintptr(addr _uintptr, val uintptr)
func StorePointer(addr _unsafe.Pointer, val unsafe.Pointer)
写入操作
func AddInt32(addr _int32, delta int32) (new int32)
func AddInt64(addr _int64, delta int64) (new int64)
func AddUint32(addr _uint32, delta uint32) (new uint32)
func AddUint64(addr _uint64, delta uint64) (new uint64)
func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)
修改操作
func SwapInt32(addr _int32, new int32) (old int32)
func SwapInt64(addr _int64, new int64) (old int64)
func SwapUint32(addr _uint32, new uint32) (old uint32)
func SwapUint64(addr _uint64, new uint64) (old uint64)
func SwapUintptr(addr _uintptr, new uintptr) (old uintptr)
func SwapPointer(addr _unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)
交换操作
func CompareAndSwapInt32(addr _int32, old, new int32) (swapped bool)
func CompareAndSwapInt64(addr _int64, old, new int64) (swapped bool)
func CompareAndSwapUint32(addr _uint32, old, new uint32) (swapped bool)
func CompareAndSwapUint64(addr _uint64, old, new uint64) (swapped bool)
func CompareAndSwapUintptr(addr _uintptr, old, new uintptr) (swapped bool)
func CompareAndSwapPointer(addr _unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)
比较并交换操作