当前位置:网站首页>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)
}
}
边栏推荐
- Use of snippets in vscode (code template)
- [转]:Apache Felix Framework配置属性
- How can the Solon framework easily obtain the response time of each request?
- Binary search basis
- 小程序直播+電商,想做新零售電商就用它吧!
- xftp7与xshell7下载(官网)
- PMP考试敏捷占比有多少?解疑
- Introduction to memory layout of FVP and Juno platforms
- Generate filled text and pictures
- C语言杂谈1
猜你喜欢
Quick sort summary
JVM call not used once in ten years
十年不用一次的JVM调用
UE fantasy engine, project structure
小程序直播+電商,想做新零售電商就用它吧!
Support multi-mode polymorphic gbase 8C database continuous innovation and heavy upgrade
lxml.etree.XMLSyntaxError: Opening and ending tag mismatch: meta line 6 and head, line 8, column 8
Web APIs DOM node
Reverse one-way linked list of interview questions
On-off and on-off of quality system construction
随机推荐
软件测试 -- 0 序
A new micro ORM open source framework
On-off and on-off of quality system construction
Haut OJ 1243: simple mathematical problems
Insert sort
Embedded database development programming (VI) -- C API
Collapse of adjacent vertical outer margins
远程升级怕截胡?详解FOTA安全升级
C language Essay 1
[turn]: OSGi specification in simple terms
小程序直播+電商,想做新零售電商就用它吧!
[to be continued] [UE4 notes] L3 import resources and project migration
When will Wei Lai, who has been watched by public opinion, start to "build high-rise buildings" again?
Haut OJ 1321: mode problem of choice sister
使用命令符关闭笔记本自带键盘命令
[sum of two numbers] 169 sum of two numbers II - enter an ordered array
支持多模多态 GBase 8c数据库持续创新重磅升级
Haut OJ 1401: praise energy
[转]:Apache Felix Framework配置属性
Bubble sort summary