Golang Mutual exclusion Lock internal implementation

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed. The go language provides a way to share resources out-of-the-box, with a mutex (sync). Mutex), sync. The 0 value of the mutex represents a non-locked, can be used directly, a goroutine obtains the mutex after the other goroutine can only wait until the Gorutine release the mutex, in the mutex structure only exposed two functions, respectively, lock and unlock, The use of mutex is very simple, this article does not elaborate.

You are using sync. Do not make a value copy of the mutex as this may cause the lock to fail. When we open our IDE and jump into our Sync.mutex code, we find that it has the following structure:

type Mutex struct {state int32     //互斥锁上锁状态枚举值如下所示sema  uint32    //信号量,向处于Gwaitting的G发送信号}const (mutexLocked = 1 << iota // 1 互斥锁是锁定的mutexWoken              // 2 唤醒锁mutexWaiterShift = iota // 2 统计阻塞在这个互斥锁上的goroutine数目需要移位的数值)

The state value above is 0 (available) 1 (locked) 2~31 waiting queue count

The following is the source of the mutex, there will be four more important methods need to be explained in advance, respectively, is runtime_canspin,runtime_dospin,runtime_semacquiremutex,runtime_semrelease,

1,Runtime_canspin: more conservative spin, Golang in the spin lock will not always spin down, in the runtime package Runtime_canspin methods to do some restrictions, To pass over the ITER large equals 4 or the CPU core number is equal to 1, the maximum logical processor is greater than 1, there is at least one local p queue, and the local p queue can run the G queue is empty.

//go:linkname sync_runtime_canSpin sync.runtime_canSpinfunc sync_runtime_canSpin(i int) bool {if i >= active_spin || ncpu <= 1 || gomaxprocs <= int32(sched.npidle+sched.nmspinning)+1 {return false}if p := getg().m.p.ptr(); !runqempty(p) {return false}return true}

2, Runtime_dospin: will call the Procyield function, the function is also assembly language implementation. The function internally loops the call to the pause instruction. The pause instruction does nothing but consumes CPU time, and when the pause instruction is executed, the CPU does not make unnecessary optimizations to it.

//go:linkname sync_runtime_doSpin sync.runtime_doSpinfunc sync_runtime_doSpin() {procyield(active_spin_cnt)}

3,Runtime_semacquiremutex:

//go:linkname sync_runtime_SemacquireMutex sync.runtime_SemacquireMutexfunc sync_runtime_SemacquireMutex(addr *uint32) {semacquire(addr, semaBlockProfile|semaMutexProfile)}

4,runtime_semrelease:

//go:linkname sync_runtime_Semrelease sync.runtime_Semreleasefunc sync_runtime_Semrelease(addr *uint32) {semrelease(addr)}

The lock function of the mutex is defined as follows


Func (M *mutex) lock () {//Use CAs to try to acquire the lock if atomic first. CompareAndSwapInt32 (&m.state, 0, mutexlocked) {//Here is-race does not need to tube it if race. Enabled {race. Acquire (unsafe. Pointer (M)}}//Successful get return Return}awoke: = False//loop flag iter: = 0//loop counter for {old: = m.state//Get current lock status NEW: = Old | mutexlocked//will be the current state the last one specified 1if old&mutexlocked! = 0 {//If so occupied if Runtime_canspin (ITER) {//check whether the spin lock can be entered if!awoke && Amp Old&mutexwoken = = 0 && Old>>mutexwaitershift! = 0 &&atomic. CompareAndSwapInt32 (&m.state, old, Old|mutexwoken) {//awoke marked as Trueawoke = Tru e}//Enter spin state runtime_dospin () Iter++continue}//Not get to lock, current G enters gwaitting state                        New = old + 1<<mutexwaitershift}if awoke {if New&mutexwoken = = 0 {throw ("Sync:inconsistent Mutex State")} Clear Mark New &^= Mutexwoken}//Update status if Atomic. CompareAndSwapInt32 (&m.state, old,New) {if old&mutexlocked = = 0 {break}//Lock request fails, goes into hibernation, waits for the signal to wake up and starts the loop run again Time_semacquiremutex (&m.sema) awoke = Trueiter = 0}}if race. Enabled {race. Acquire (unsafe. Pointer (M))}}

The unlock function of the mutex is defined as follows

func (m *Mutex) Unlock() {if race.Enabled {_ = m.staterace.Release(unsafe.Pointer(m))}// 移除标记new := atomic.AddInt32(&m.state, -mutexLocked)if (new+mutexLocked)&mutexLocked == 0 {throw("sync: unlock of unlocked mutex")}old := newfor {//当休眠队列内的等待计数为0或者自旋状态计数器为0,退出if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken) != 0 {return}// 减少等待次数,添加清除标记new = (old - 1<<mutexWaiterShift) | mutexWokenif atomic.CompareAndSwapInt32(&m.state, old, new) {                        // 释放锁,发送释放信号runtime_Semrelease(&m.sema)return}old = m.state}}

There is no conflict between the mutex is the simplest case, there is a conflict, the first spin, because most of the mutex protection of the code snippet is very short, after a short spin can be obtained, if the spin wait for no results, it is necessary to use the semaphore to let the current goroutine into the gwaitting state.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.