当前位置:网站首页>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)
}
}
边栏推荐
- Solon 框架如何方便获取每个请求的响应时间?
- UE fantasy engine, project structure
- Reverse one-way linked list of interview questions
- Simple modal box
- 二十六、文件系统API(设备在应用间的共享;目录和文件API)
- Pointnet++的改进
- [sum of two numbers] 169 sum of two numbers II - enter an ordered array
- 使用命令符关闭笔记本自带键盘命令
- On-off and on-off of quality system construction
- [to be continued] I believe that everyone has the right to choose their own way of life - written in front of the art column
猜你喜欢
[interval problem] 435 Non overlapping interval
Yolov5 adds attention mechanism
Learning notes of "hands on learning in depth"
Research on the value of background repeat of background tiling
[turn to] MySQL operation practice (I): Keywords & functions
Generate filled text and pictures
Binary search basis
Pointnet++的改进
[paper notes] multi goal reinforcement learning: challenging robotics environments and request for research
支持多模多态 GBase 8c数据库持续创新重磅升级
随机推荐
Embedded database development programming (VI) -- C API
To be continued] [UE4 notes] L4 object editing
National teacher qualification examination in the first half of 2022
MySQL数据库(一)
Remote upgrade afraid of cutting beard? Explain FOTA safety upgrade in detail
Improvement of pointnet++
When will Wei Lai, who has been watched by public opinion, start to "build high-rise buildings" again?
Haut OJ 1241: League activities of class XXX
Insert sort
[turn]: Apache Felix framework configuration properties
UE fantasy engine, project structure
Applet Live + e - commerce, si vous voulez être un nouveau e - commerce de détail, utilisez - le!
Developing desktop applications with electron
Heap sort summary
64 horses, 8 tracks, how many times does it take to find the fastest 4 horses at least
win10虚拟机集群优化方案
[转]MySQL操作实战(三):表联结
SAP-修改系统表数据的方法
FVP和Juno平台的Memory Layout介绍
Es module and commonjs learning notes