一般其它高级语言,死锁的表现都为程序卡死,但是go则不然,它有可能不卡死有可能卡死,则分情况看
我们来看看死锁的两个情况
- 死锁直接panic报all goroutines are asleep - deadlock!
import (
"fmt"
"sync"
)
func main() {
var lock sync.RWMutex
lock.Lock()
fmt.Println("this is test examples")
lock.Lock()
lock.Unlock()
lock.Unlock()
}
- 死锁直接卡死
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var lock sync.RWMutex
lock.Lock()
go func() {
// time.Sleep也可以看作是一个timer,它的实现见E:\WorkSpace\github\go1.14.6-analysis\src\runtime\time.go函数func timeSleep()
time.Sleep(10000 * time.Second)
}()
fmt.Println("this is test examples")
lock.Lock()
lock.Unlock()
lock.Unlock()
}
源码中见分晓
让我们来看看go sdk的源码,见E:\WorkSpace\github\go1.14.6-analysis\src\runtime\proc.go
中的checkdead函数,这里我们只截取部分代码端,大致调用链为func main() ->func systemstack()->func sysmon()->func checkded()
func checkdead(){
...
var run0 int32
if !iscgo && cgoHasExtraM {
mp := lockextra(true)
haveExtraM := extraMCount > 0
unlockextra(mp)
if haveExtraM {
run0 = 1
}
}
// 由上面的逻辑知道run0 只能为1或者0
// 这里的是获取正在运行的M的数量,它等于总m个数-当前等待工作的空闲 m 计数-当前等待工作的被 lock 的 m 计数-系统m的数量
run := mcount() - sched.nmidle - sched.nmidlelocked - sched.nmsys
if run > run0 {
return
}
if run < 0 {
print("runtime: checkdead: nmidle=", sched.nmidle, " nmidlelocked=", sched.nmidlelocked, " mcount=", mcount(), " nmsys=", sched.nmsys, "\n")
throw("checkdead: inconsistent counts")
}
//如果上面的两个if判断都未命中,则run的个数要么大于1要么大于0
...
// There are no goroutines running, so we can look at the P's.
for _, _p_ := range allp {
// 这里的allp就是runtime 的 全局 P 队列 (存放所有 P),遍历每个p上的timers的个数,只要有一个p的timer的个数大于0都不会触发all goroutines are asleep - deadlock
if len(_p_.timers) > 0 {
return
}
}
getg().m.throwing = -1 // do not dump full stacks
unlock(&sched.lock) // unlock so that GODEBUG=scheddetail=1 doesn't hang
throw("all goroutines are asleep - deadlock!")
}
综上死锁直接panic报all goroutines are asleep - deadlock!的直接原因还是所有全局p队列中的每个p上的timer个数小于等于0了,如果有哪里说的不对,还请各位大佬不吝赐教!