sync.Once
*sync.OnceDo(f func())func()
sync.Onceoo.Do()(&o).Do()o.Do()o.Do()
sync.Once

一个例子:

Helloworld!Helloworld!
sync.Mutexsync.RWMutex
*sync.Mutex*sync.RWMutexsync.Locker接口类型。 所以这两个类型都有两个方法:Lock()和Unlock(),用来保护一份数据不会被多个使用者同时读取和修改。Lock()Unlock()
Lock()Unlock()*sync.RWMutexRLock()RUnlock()

(注意:这里的数据读取者*和**数据写入者***不应该从字面上理解。有时候某些数据读取者可能修改数据,而有些数据写入者可能只读取数据。)

MutexMutexMutexmm.Lock()mm.Lock()Mutexm.Unlock()
m.Lock()m.Unlock()(&m).Lock()(&m).Unlock()
sync.Mutex
CounterMutexn
RWMutexRWMutexrwmrwm.Lock()rwmrwm.RLock()rwmrwm.Unlock()rwm.RUnlock()rwmrwmrwm.RLock()rwm.Unlock()rwmrwm
rwm.Lock()rwm.Unlock()rwm.RLock()rwm.RUnlock()(&rwm).Lock()(&rwm).Unlock()(&rwm).RLock()(&rwm).RUnlock()
RWMutexrwm
rwmrwmrwmrwmrwmrwmrwmrwm

后两条规则是为了确保数据读取者和写入者都有机会执行它们的操作。

请注意:一个锁并不会绑定到一个协程上,即一个锁并不记录哪个协程成功地加锁了它。 换句话说,一个锁的加锁者和此锁的解锁者可以不是同一个协程,尽管在实践中这种情况并不多见。

ValueIncreaseCountermsync.RWMutex
sync.RWMutex
abdc
time.Sleep
sync.Mutexsync.RWMutexMutex
HiByesync.Mutexsync.RWMutex


sync.Cond
sync.Cond
sync.Condsync.LockerL*sync.Mutex*sync.RWMutex
*sync.CondWait()Signal()Broadcast()
CondCondc
  • c.Wait()
    必须在
    c.L
    字段值的锁处于加锁状态的时候调用;否则,
    c.Wait()
    调用将造成一个恐慌。 一个
    c.Wait()
    调用将
cc.L.Unlock()c.Lc.Signal()c.Broadcast()


c.Signal()cc.Broadcast()c
c.Wait()c.Signal()c.Broadcast()(&c).Wait()(&c).Signal()(&c).Broadcast()
c.Signal()c.Broadcast()c.Wait()
sync.Cond

一个可能的输出:

4 原子操作

整数原子操作

本文的余下部分将通过一些示例来展示如何使用这些原子操作函数。

Addint32n11000
atomic.AddInt32(&n, 1)n++1000
atomic.Int32


StoreTLoadT

下面这个例子使用了Go 1.19引入的类型和方法:


Tint32int64AddTTuint32uint64uintptrAddTTsync/atomicSubstractTT
Tv-v-vAddTc-cAddT^T(c-1)AddT
SwapTStoreT
CompareAndSwapTtruefalse

一个例子:

下面是与之对应的使用Go 1.19引入的类型和方法的实现:


请注意,到目前为止(Go 1.20),一个64位字(int64或uint64值)的原子操作要求此64位字的内存地址必须是8字节对齐的。 对于Go 1.19引入的原子方法操作,此要求无论在32-bit还是64-bit架构上总是会得到满足,但是对于32-bit架构上的原子函数操作,此要求并非总会得到满足。 请阅读关于Go值的内存布局一文获取详情。