当前位置:网站首页>sync. Interpretation of mutex source code
sync. Interpretation of mutex source code
2022-07-05 05:24:00 【. fried eggs with tomatoes】
Unlocked
package main
import (
"fmt"
"sync"
"time"
)
func main() {
x := 0
now := time.Now()
var group sync.WaitGroup
group.Add(1000)
for i := 0; i < 1000; i++ {
go func() {
x++
defer group.Done()
}()
}
// Wait for all goroutine Finish after execution
group.Wait()
fmt.Println(x)
fmt.Println(time.Now().UnixMilli() - now.UnixMilli())
}

The normal expectation should be 1000; But because we didn't lock it ,goroutineA While writing ,goroutineB I'm writing, too , Read at the same time , There will be two. goroutine Read the same value , Then go ahead and do x++, Only 983
Lock
package main
import (
"fmt"
"sync"
"time"
)
func main() {
x := 0
now := time.Now()
var group sync.WaitGroup
var mutex sync.Mutex
group.Add(1000)
for i := 0; i < 1000; i++ {
go func() {
mutex.Lock()
x++
defer mutex.Unlock()
defer group.Done()
}()
}
// Wait for all goroutine Finish after execution
group.Wait()
fmt.Println(x)
fmt.Println(time.Now().UnixMilli() - now.UnixMilli())
}

Source code
Mutex
// Mutex It's a kind of mutex .
// Mutually exclusive 0 Value is an unlocked mutex .
// Mutexes cannot be copied after the first use
type Mutex struct {
state int32 // Indicates the status of the current lock
sema uint32 // Semaphore variable , Used to control goroutine Blocking sleep and wakeup
}
// state state
const (
mutexLocked = 1 << iota // Locking state of mutex
mutexWoken // Wake up from normal mode
mutexStarving // Currently, the mutex is hungry
mutexWaiterShift = iota // Waiting for the goroutine Number `
starvationThresholdNs = 1e6
)
- Mutex Two modes of operation
- Normal mode ( Not fair lock )
- In normal mode , The waiting person follows FIFO( fifo ) Sequential queuing , But wake up goroutine Don't have a lock , But with the newly arrived goroutines Fight for the ownership of the lock , Newly added goroutines There is an advantage :** They are already CPU Up operation , It could be one or more ,** But it will only awaken one goroutine, Wake up this goroutine It is very likely to lose , under these circumstances , It is in front of the waiting queue . If a person waits longer than 1ms, It will switch from normal mode to hungry mode
- Hunger mode ( Fair lock )
- In hunger mode , The ownership of the mutex is directly from the one being unlocked goroutine To the front of the queue goroutine, The new arrival goroutines Will not attempt to acquire the lock , Will not try to spin , They will put themselves at the end of the waiting queue
- If you get the lock goroutine It's the last one in the queue goroutine, It will switch from starvation mode to normal mode
- If you get the lock goroutine The waiting time is less than 1ms, It will switch from starvation mode to normal mode
- The performance of normal mode is much better , Because even if there is blockage goroutines,goroutine You can also obtain locks several times in a row , Hunger mode solves the problem of lock fairness , But performance will degrade , Switching back and forth between normal mode and starvation mode is actually a balance mode between performance and fairness
- Normal mode ( Not fair lock )
Mutex.Lock
// Lock method , If you lock repeatedly, an error will be reported
func (m *Mutex) Lock() {
// adopt CAS Judge the status of the current lock ;
// If the lock is free , namely m.state==0
// Then lock it , namely m.state=1
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
if race.Enabled {
race.Acquire(unsafe.Pointer(m))
}
return
}
// The current lock has been locked by another goroutine Get
m.lockSlow()
}
func (m *Mutex) lockSlow() {
// Used to calculate the waiting time
var waitStartTime int64
// Hunger mode marker , If the waiting time exceeds 1ms,starving Set to true
// Subsequent operations will m Also marked as hungry
starving := false
// At present goroutine Whether to wake up
// When goroutine In spin , amount to CPU There are already waiting on goroutine
// To avoid m When unlocking, it wakes up others goroutine, Spin with m Set to wake up state
// m When awake ,awoke Also set to true
awoke := false
// Number of spins
iter := 0
// Current lock state
old := m.state
for {
// Determine whether you can enter spin
// 1. Currently in non hunger mode
// 2.runtime_canSpin(iter); The number of spins is less than 4
if old&(mutexLocked|mutexStarving) == mutexLocked && runtime_canSpin(iter) {
// !awoke: Judge the present goroutine Not in a wakeup state
// old&mutexWoken == 0: There is currently no other awakening goroutine
// old>>mutexWaiterShift != 0: There are waiting in the waiting queue goroutine
// atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken): Try to lower the current lock 2 Bit Woken The status bit is set to 1, Indicates that has been awakened , This is to notify when unlocking Unlock() Don't wake up the others goroutine 了
if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 &&
atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) {
// Will the current goroutine It's wake up
awoke = true
}
// Spin
runtime_doSpin()
// Number of spins ++
iter++
// Record the status of the current lock
old = m.state
continue
}
// be based on old Declare a new state
new := old
// Don't try to get hungry mutexes , The new arrival goroutine You have to line up
if old&mutexStarving == 0 {
new |= mutexLocked
}
// If old Locked or in collective mode , according to FIFO line up
if old&(mutexLocked|mutexStarving) != 0 {
new += 1 << mutexWaiterShift
}
// If m In hunger mode , will starving Set as 1 Express hunger
if starving && old&mutexLocked != 0 {
new |= mutexStarving
}
if awoke {
// Wake up state operation
if new&mutexWoken == 0 {
// Mutually exclusive state is inconsistent , Throw an exception
throw("sync: inconsistent mutex state")
}
// The new status clears the wake-up flag
new &^= mutexWoken
}
// Try to update the state of the lock to the desired state
if atomic.CompareAndSwapInt32(&m.state, old, new) {
if old&(mutexLocked|mutexStarving) == 0 {
// If the original lock is not locked and is not in hunger mode
// At present goroutine Got the lock , direct break
break
}
// The reverse lock has not been obtained ;waitStartTime It's just waiting time
// If not for 0 Just put it on the right side
queueLifo := waitStartTime != 0
// by 0 Put it at the end of the team
if waitStartTime == 0 {
waitStartTime = runtime_nanotime()
}
// Block waiting
runtime_SemacquireMutex(&m.sema, queueLifo, 1)
// After wake up , Check whether it is in hunger mode
// 1. Currently in hunger mode
// 2. Wait more than 1ms
starving = starving || runtime_nanotime()-waitStartTime > starvationThresholdNs
// Once again m The state of
old = m.state
if old&mutexStarving != 0 {
if old&(mutexLocked|mutexWoken) != 0 || old>>mutexWaiterShift == 0 {
// The current lock is neither acquired nor awakened , Or the waiting queue is empty
// It means that there is a mutually exclusive state inconsistency
throw("sync: inconsistent mutex state")
}
// At present goroutine Got the lock , Waiting in line -1
delta := int32(mutexLocked - 1<<mutexWaiterShift)
if !starving || old>>mutexWaiterShift == 1 {
// There is only one non hungry mode or queue left goroutine Out of hunger mode
delta -= mutexStarving
}
// Update the status value and break
atomic.AddInt32(&m.state, delta)
break
}
// Set the state to wakeup ; And reset the number of spins
awoke = true
iter = 0
} else {
// Lock by others goroutine Occupy , Restore status continues for loop
old = m.state
}
}
if race.Enabled {
race.Acquire(unsafe.Pointer(m))
}
}
Mutex.Unlock
// Unlocking method , If you unlock without locking, an error will be reported
func (m *Mutex) Unlock() {
if race.Enabled {
_ = m.state
race.Release(unsafe.Pointer(m))
}
// take m.state Set as 0, And copy it to new
// If new by 0; Indicates successful unlocking ; immediate withdrawal
new := atomic.AddInt32(&m.state, -mutexLocked)
if new != 0 {
// new Not for 0; Indicates that the current lock is not occupied , But there is waiting goroutine Not awakened
m.unlockSlow(new)
}
}
func (m *Mutex) unlockSlow(new int32) {
// The current lock is not locked , If you unlock it directly, an error will be reported
if (new+mutexLocked)&mutexLocked == 0 {
throw("sync: unlock of unlocked mutex")
}
if new&mutexStarving == 0 {
// Normal mode
old := new
for {
// There is no waiting in the queue goroutine Go straight back to
// If the lock is in the locked state ; On behalf of goroutine Get lock ; Go straight back to
// If the lock is in the wakeup state ; On behalf of goroutine Awakened , Go straight back to
// If you are currently in hunger mode , After locking, it will follow FIFO The rules are for the right goroutine
if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken|mutexStarving) != 0 {
return
}
// Wake up the lock , Waiting in line -1
new = (old - 1<<mutexWaiterShift) | mutexWoken
if atomic.CompareAndSwapInt32(&m.state, old, new) {
// A successful preemption wakes up a goroutine return
runtime_Semrelease(&m.sema, false, 1)
return
}
// Preemption is unsuccessful ; Update status information ; continue for
old = m.state
}
} else {
// Hunger mode
// Directly in accordance with the FIFO The rules are for the right goroutine
runtime_Semrelease(&m.sema, true, 1)
}
}
边栏推荐
猜你喜欢

On-off and on-off of quality system construction

GBase数据库助力湾区数字金融发展

Embedded database development programming (VI) -- C API

服务熔断 Hystrix

【论文笔记】Multi-Goal Reinforcement Learning: Challenging Robotics Environments and Request for Research

Learning notes of "hands on learning in depth"

YOLOv5-Shufflenetv2

Generate filled text and pictures

Shell Sort

win10虚拟机集群优化方案
随机推荐
Reflection summary of Haut OJ freshmen on Wednesday
小程序直播+電商,想做新零售電商就用它吧!
Under the national teacher qualification certificate in the first half of 2022
Haut OJ 1401: praise energy
A three-dimensional button
小程序直播+电商,想做新零售电商就用它吧!
[turn to] MySQL operation practice (I): Keywords & functions
TF-A中的工具介绍
BUUCTF MISC
使用Electron开发桌面应用
嵌入式数据库开发编程(六)——C API
C语言杂谈1
Haut OJ 1245: large factorial of CDs --- high precision factorial
Solon Logging 插件的添加器级别控制和日志器的级别控制
[turn]: OSGi specification in simple terms
[to be continued] I believe that everyone has the right to choose their own way of life - written in front of the art column
Magnifying glass effect
2022上半年全国教师资格证下
Demonstration of using Solon auth authentication framework (simpler authentication framework)
Bucket sort