The go language provides an out-of-the-box way to share resources. Mutex (sync.Mutex), the zero value of sync.Mutex indicates that it is not locked and can be used directly. After a goroutine acquires the mutex Other goroutines can only wait until the goroutine releases the mutex. Only two functions are exposed in the Mutex structure, namely Lock and Unlock. It is very simple to use the mutex, and this article does not describe the use.
Never make a value copy when using sync.Mutex, as this may invalidate the lock. When we open our IDE and jump into our sync.Mutex code we will find it has the following structure:
The following is the source code of the mutex. There are four important methods that need to be explained in advance, namely runtime_canSpin, runtime_doSpin, runtime_SemacquireMutex, runtime_Semrelease,
1. Runtime_canSpin: a relatively conservative spin. The spin lock in golang does not spin all the time. The runtime_canSpin method in the runtime package has some restrictions. The passed iter is greater than or equal to 4 or the number of cpu cores is less than or equal to 1, and the maximum The logical processor is greater than 1, there is at least a local P queue, and the local P queue can run the G queue is empty.
//go:linkname sync_runtime_canSpin sync.runtime_canSpin func 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 (give out the thread), which is also implemented in assembly language. The function inner loop calls the PAUSE instruction. The PAUSE instruction does nothing, but consumes CPU time, and the CPU does not unnecessarily optimize the PAUSE instruction when it is executed.
//go:linkname sync_runtime_doSpin sync.runtime_doSpin func sync_runtime_doSpin() { procyield(active_spin_cnt) }
3、runtime_SemacquireMutex:
//go:linkname sync_runtime_SemacquireMutex sync.runtime_SemacquireMutex func sync_runtime_SemacquireMutex(addr *uint32) { semacquire(addr, semaBlockProfile|semaMutexProfile) }
4、runtime_Semrelease:
//go:linkname sync_runtime_Semrelease sync.runtime_Semrelease func sync_runtime_Semrelease(addr *uint32) { semrelease(addr) }
The above is the whole content of this article, I hope it will be helpful to everyone's learning, and I hope everyone will support Scripting Home.
Reprinted from https://zhuanlan.zhihu.com/p/27608263