go的mutex如何實現?
阿新 • • 發佈:2019-03-18
已知
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的排程
- 有記憶體屏障
驗證
// 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 }
// 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) }
// 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) // 這裡是協程排程