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