这是死锁吗?为什么执行程序会说它是死锁?

时间:2019-11-19 13:38:02

标签: go deadlock

当我运行此程序时:

package main

import (
    "fmt"
    "sync"
    "time"
)

var mu = new(sync.Mutex)

func f2() {
    mu.Lock()
    fmt.Println("call f2...")
}

func main() {
    go f2()
    time.Sleep(time.Second * 2)
    mu.Lock()
    fmt.Println("get lock in main")
}

我得到以下输出:

call f2...
fatal error: all goroutines are asleep - deadlock!

playground link

我们知道具有死锁的条件有四种,一种是Hold and wait or resource holding,它要求至少有两种资源才能获得,但是这里只有一种资源。

那么这是一个死锁吗?

我知道退出了很多并发编程。只是在这种情况下,只有f2()个不释放锁定,并且不像wikipedia defines那样死锁。

1 个答案:

答案 0 :(得分:3)

事前注意::“ goroutine死锁”(这是您遇到的情况)是所有现有goroutine被阻止并且无法自行执行(永远)的状态。有多个现有的goroutine或只有一个goroutine都没关系。如果go运行时检测到goroutine死锁,则终止应用程序(没有意义让该应用程序永久挂起,它将永远无法恢复,这在goroutine死锁的定义中)。


您的代码中有一个互斥锁,该互斥锁已锁定在2个goroutine中:在main goroutine中,而另一个在执行f2()中。

如果f2()越早锁定互斥锁,主goroutine将永远无法再次锁定它,因为没有人解锁互斥锁,因此这是一个死锁。因为f2()将在锁定和打印后返回,并且其goroutine将结束,并且仅剩下的main goroutine在尝试锁定mu时被阻止。并且由于您在main()(作为goroutine)和锁定互斥锁之间的f2中使用了2秒的睡眠,因此您会像往常一样观察该死锁。

>

请注意,如果安排main goroutine首先锁定互斥锁,则不会发生死锁,因为main()函数可以继续运行,并且一旦返回,应用程序将终止(不等待f2()完成)。但是同样,由于您插入了睡眠,您可能永远不会获得此结果。

我使用了“总是喜欢” “不可能永远” 这样的短语,因为尽管睡眠指令对于goroutine调度程序来说是一个很好的调度点,但它不是同步点。运行时将安排其他goroutine在goroutine处于睡眠状态时运行(为什么不这样做),但这不能保证。同步点可以为您提供保证(例如通道通信,锁定,sync.Once等)。这在The Go Memory Model中有详细说明。

相关问题