package sync
import "sync"
syncOnceWaitGroupchannel
注意:本包的类型的值不应被拷贝。
sync.WaitGroup
type WaitGroup struct {
// 包含隐藏或非导出字段
}
WaitGroupAddDoneWaitWaitGroup0AddDoneWait
func (wg *WaitGroup) Add(delta int)Adddeltadelta0Wait0panicfunc (wg *WaitGroup) Done()DoneWaitGroup1func (wg *WaitGroup) Wait()WaitWaitGroup0
示例:
func f(wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(time.Second)
fmt.Printf("我是f\n")
}
func main() {
wg := sync.WaitGroup{}
wg.Add(1)
go f(&wg)
fmt.Println("我是main")
wg.Wait()
}
WaitGroupmaingoroutineWaitGroup
sync.Mutex:互斥锁
type Mutex struct {
// 包含隐藏或非导出字段
}
MutexMutex
Mutex
func (m *Mutex) Lock()Lockmmmfunc (m *Mutex) Unlock()Unlockmm
goroutine
先举一个不加互斥锁的例子:
var x int64
var wg sync.WaitGroup
func add() {
for i := 0; i < 5000; i++ {
x = x + 1
}
wg.Done()
}
func main() {
wg.Add(2)
go add()
go add()
wg.Wait()
fmt.Println(x)
}
goroutinex
var x int64
var wg sync.WaitGroup
var lock sync.Mutex
func add() {
for i := 0; i < 5000; i++ {
lock.Lock() // 在访问共享资源前上锁
x = x + 1 // 上锁后其它goroutine无法访问x
lock.Unlock() // 访问完就解锁
}
wg.Done()
}
func main() {
wg.Add(2)
go add()
go add()
wg.Wait()
fmt.Println(x)
}
Mutex
sync.RWMutex:读写互斥锁
type RWMutex struct {
// 包含隐藏或非导出字段
}
RWMutexRWMutexRWMutexgoroutinegoroutinegoroutinegoroutine
RWMutex
func (rw *RWMutex) Lock()Lockrwfunc (rw *RWMutex) Unlock()Unlockrwrwfunc (rw *RWMutex) RLock()RLockrwfunc (rw *RWMutex) RUnlock()Runlockrwrwfunc (rw *RWMutex) RLocker() LockerRlockerrw.Rlockrw.RunlockLockerLockUnlock
goroutinegoroutine
sync.Once:保证函数只执行一次
type Once struct {
done uint32 // 表示函数是否已执行
m Mutex // 执行函数时需要上锁,保证只执行一次
}
OnceDo
func (o *Once) Do(f func())
DofOnceDoffOnceff
config.once.Do(func() { config.init(filename) })
fDofDo
示例:
var once sync.Once
onceBody := func() {
fmt.Println("Only once")
}
done := make(chan bool)
for i := 0; i < 10; i++ {
go func() {
once.Do(onceBody)
done <- true
}()
}
for i := 0; i < 10; i++ {
<-done
}
输出:
Only once
sync.Oncef
sync.Map:并发安全的映射
type Map struct {
// 包含隐藏或非导出字段
}
Mapmap[interface{}]interface{}goroutine
Map
Map
Map
Map
func (m *Map) Delete(key interface{})Deletekeyfunc (m *Map) Load(key interface{}) (value interface{}, ok bool)Loadkeynilokfunc (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool)LoadAndDeletekeyloadedkeyfunc (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)LoadOrStorekeykeyvaluevalueloadedtruefalsefunc (m *Map) Range(f func(key, value interface{}) bool)RangeMapfkeyvaluebooltruekey-valuefalsefunc (m *Map) Store(key, value interface{})Store
Range
var m1 sync.Map
m1.Store(1, 1)
m1.Store(2, 2)
m1.Store(3, 3)
f := func(k, v interface{}) bool {
fmt.Println(k, v)
if k == 3 {
return false
}
return true
}
m1.Range(f)
输出:
2 2
3 3
因为遍历是无序的,所以输出只是其中一次输出,可以看到遍历到3就结束了。
package atomic
import "sync/atomic"
代码中的加锁操作因为涉及内核态的上下文切换会比较耗时、代价比较高。针对基本数据类型我们还可以使用原子操作来保证并发安全,因为原子操作是Go语言提供的方法,它在用户态就可以完成,因此性能比加锁操作更好。
atomic
这些函数必须谨慎地保证正确使用。除了某些特殊的底层应用,使用通道或者sync包的函数/类型实现同步更好。
应通过通信来共享内存,而不通过共享内存实现通信。
atomic
读取系列
*addr
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)
写入系列
val*addr
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)
修改系列(加减操作)
delta*addr
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)
AddUint32AddUint64AddUint64xcAddUint64(&x, ^uint64(c-1))xAddUint64(&x, ^uint64(0))
交换系列
new*addr*addr
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)
比较并交换系列
*addroldnew*addr
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)
参考
标准库中文文档
标准库英文文档/sync包
博客1
博客2