为什么80%的码农都做不了架构师?>>>
已知
c++中多线程的mutex,是通过futex来实现原子操作+线程唤醒的,然后再加上memory barrier(内存序)来保证内存可见性的。即:
mutex = futex + memory barrier = atomic + thread schedule + memory barrier
详情可参见这篇博客:pthread_mutex_lock实现原理
新问题
go中多协程的mutex,如何实现?
猜测
goroutine mutex = atomic + goroutine schedule + memory barrier
即有3个条件:
- 有原子操作
- 有goroutine的调度
- 有内存屏障
验证
sync/mutex.go#L69
// Lock locks m.
// If the lock is already in use, the calling goroutine
// blocks until the mutex is available.
func (m *Mutex) Lock() {
// Fast path: grab unlocked mutex.
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) { // 这里是原子操作
if race.Enabled {
race.Acquire(unsafe.Pointer(m)) // 这里是成功后调用内存屏障
}
return
}
// Slow path (outlined so that the fast path can be inlined)
m.lockSlow() // 这里是原子操作失败,可能会“阻塞”的场景。调用了runtime_SemacquireMutex
}
runtime/race.go#L28
// RaceAcquire/RaceRelease/RaceReleaseMerge establish happens-before relations
// between goroutines. These inform the race detector about actual synchronization
// that it can't see for some reason (e.g. synchronization within RaceDisable/RaceEnable
// sections of code).
// RaceAcquire establishes a happens-before relation with the preceding
// RaceReleaseMerge on addr up to and including the last RaceRelease on addr.
// In terms of the C memory model (C11 §5.1.2.4, §7.17.3),
// RaceAcquire is equivalent to atomic_load(memory_order_acquire). // 这里是内存屏障
func RaceAcquire(addr unsafe.Pointer) {
raceacquire(addr)
}
sync/runtime.go#L16
// Semrelease atomically increments *s and notifies a waiting goroutine
// if one is blocked in Semacquire.
// It is intended as a simple wakeup primitive for use by the synchronization
// library and should not be used directly.
// If handoff is true, pass count directly to the first waiter.
// skipframes is the number of frames to omit during tracing, counting from
// runtime_Semrelease's caller.
func runtime_Semrelease(s *uint32, handoff bool, skipframes int) // 这里是协程调度
到此结束。