1. 程式人生 > >go的mutex如何實現?

go的mutex如何實現?

開發十年,就只剩下這套架構體系了! >>>   

已知

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個條件:

  1. 有原子操作
  2. 有goroutine的排程
  3. 有記憶體屏障

驗證

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) // 這裡是協程排程