Go语言学习笔记part7
并发2
互斥锁
Go语言中使用sync
包的Mutex
类型来实现互斥锁
var x int64
var wg sync.WaitGroup
var lock sync.Mutex
func add() {
for i := 0; i < 5000; i++ {
lock.Lock() // 加锁
x = x + 1
lock.Unlock() // 解锁
}
wg.Done()
}
func main() {
wg.Add(2)
go add()
go add()
wg.Wait()
fmt.Println(x)
}
总结一下,Go语言中的互斥锁在sync包中,操作如下:
import "sync"
var lock sync.Mutex
...
lock.Lock()
//临界区资源操作
lock.Unlock()
...
读写锁
import "sync"
var rwlock sync.RWMutex
...
rwlock.Lock()
//写操作
lock.Unlock()
...
rwlock.RLock()
//读操作
rwlock.RUnlock()
...
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
方法,其签名如下:
func (o *Once) Do(f func()) {}
备注:如果要执行的函数f
需要传递参数就需要搭配闭包来使用。
import "sync"
var doOnce sync.Once
doOnce.Do()
sync.Map
Go语言中内置的map不是并发安全的。Go语言的sync
包中提供了一个开箱即用的并发安全版map—sync.Map
。开箱即用表示不用像内置的map一样使用make函数初始化就能直接使用。同时sync.Map
内置了诸如Store
、Load
、LoadOrStore
、Delete
、Range
等操作方法。
var m = sync.Map{}
func main() {
wg := sync.WaitGroup{}
for i := 0; i < 20; i++ {
wg.Add(1)
go func(n int) {
key := strconv.Itoa(n)
m.Store(key, n)
value, _ := m.Load(key)
fmt.Printf("k=:%v,v:=%v\n", key, value)
wg.Done()
}(i)
}
wg.Wait()
}
原子操作
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) |
比较并交换操作 |