👣 Расскажите про внутреннее устройство mutex



Мьютекс гораздо более комплексная структура по сравнению с atomic, чем может показаться на первый взгляд. Прежде всего у нас есть два вида мютексов: sync.Mutex и sync.RWMutex. У каждого из них есть методы Lock и Unlock, у RWMutex есть дополнительно метод RLock (блокирование для чтения). В обоих типах методы Lock довольно долгие по сравнению с атомиками.



У Mutex код метода Lock включает два вида блокировки. Первый вариант - это когда удается захватить не заблокированный мьютекс, второй вариант довольно долгий и он запускается если мьютекс заблокирован:



// 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()

}



Первый вариант довольно быстрый и включает в себя одну atomic операцию. Второй вариант включает довольно много кода и здесь подробно разбираться не будет: там также используются атомики в процессе блокировки, но очевидно, что он исполняется еще дольше первого варианта (Fast path).



У RWMutex код метода Lock включает в себя вызов метода Lock структуры Mutex:



// Lock locks rw for writing.

// If the lock is already locked for reading or writing,

// Lock blocks until the lock is available.

func (rw *RWMutex) Lock() {

if race.Enabled {

_ = rw.w.state

race.Disable()

}

// First, resolve competition with other writers.

rw.w.Lock()

// Здесь пропущена часть кода ...

}

}



Да и сама структура RWMutex включает в себя структуру Mutex как одно из полей:



type RWMutex struct {

w Mutex // held if there are pending writers

// Здесь пропущена часть кода ...

}




Метод RLock у RWMutex гораздо быстрее и содержит меньше кода, но тем не менее не быстрее атомика, который там задействован:



func (rw *RWMutex) RLock() {

// Здесь пропущена часть кода ...

if atomic.AddInt32(&rw.readerCount, 1) < 0 {

// A writer is pending, wait for it.

runtime_SemacquireMutex(&rw.readerSem, false, 0)

}

//




📌Вопрос что производительнее атомики или мьютексы?



Напишите свой ответ в комментариях👇



@golang_interview