注意: 本文基于Golang 1.17.1

1.引言

同步原语同步原语
同步原语
锁类型 Golang Linux
互斥锁 runtime.mutex (低阶)
sync.Mutex (高阶)
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
信号量 runtime.semaphore #include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
int semctl(int semid, int semnum, int cmd, …);
int semop(int semid, struct sembuf *sops, size_t nsops);
条件变量 sync.Cond #include <pthread.h>
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

3.依赖关系

3.1 runtime.mutex

func lock(l *mutex) {
    lockWithRank(l, getLockRank(l))
}

func unlock(l *mutex) {
    unlockWithRank(l)
}

下图中

futex

3.2 runtime.semaphore

//go:linkname sync_runtime_Semacquire sync.runtime_Semacquire
func sync_runtime_Semacquire(addr *uint32) {
    semacquire1(addr, false, semaBlockProfile, 0)
}

//go:linkname sync_runtime_Semrelease sync.runtime_Semrelease
func sync_runtime_Semrelease(addr *uint32, handoff bool, skipframes int) {
    semrelease1(addr, handoff, skipframes)
}
_Gwaiting_Grunnableruntime.semaphoreruntime.mutexruntime.semaphore

3.3 sync.Mutex

sync.Mutexruntime.semaphoresync.Mutex

3.4 sync.Cond

sync.CondLsync.Mutexsync.Condruntime.mutex

4.总结

仔细的看完Golang的各种同步原语后,萌叔有几个感受

4.1 Golang中的同步原语大多数能够找到对应的Linux系统调用

由于Linux系统调用通常开销很大,为了实现中极力的避免使用Linux系统调用,Golang在语言层面(用户空间)重新实现了这些系统调用。

gopark()goready()

4.2 高阶的同步原语的实现依赖低阶的同步原语

sync.Condsync.Mutexruntime.mutex

5. 参考资料

1.futex
2.详解linux多线程——互斥锁、条件变量、读写锁、自旋锁、信号量
3.mutex
4.sched_yield
5.go中semaphore(信号量)源码解读
6.semget
7.semaphore的原理与实现
8.pthread_cond_wait

6. 后记

Golang为了避免使用锁,还有一些独特的技巧。比如同一个P上的多个G,竞争访问某些临界数据时,可以完全不用加锁。

  • P上的多个G共用一个runq (runnable queue存放就绪状态的G)
  • P上的多个G共用一个pool (sync.Pool)

请我喝瓶饮料

微信支付码