注意: 本文基于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)