【问题标题】:golang: why there's no deadlock in this code?golang:为什么这段代码没有死锁?
【发布时间】:2021-06-20 02:29:32
【问题描述】:

golang:为什么这段代码没有死锁?

请浏览以下代码:

package main
  

import (
    "fmt"
    "time"
)


func f1(done chan bool) {
    done <- true
    fmt.Printf("this's f1() goroutine\n")
}

func f2(done chan bool) {
    val := <-done
    fmt.Printf("this's f2() goroutine. val: %t\n", val)
}


func main() {
    done1 := make(chan bool)
    done2 := make(chan bool)

    go f1(done1)
    go f2(done2)
    
    fmt.Printf("main() go-routine is waiting to see deadlock.\n")
    time.Sleep(5 * time.Second)
}

正如所见,go-routine f1() 正在向通道发送一个值。 go-routine f2() 正在从通道接收一个值。 但是,没有从 go-routine f1() 发送到的通道接收 go-routine。 同样,没有 go-routine 发送到 go-routine f2() 正在接收的通道上。

【问题讨论】:

  • 只是因为当 5 秒的睡眠过去后,main goroutine 可以继续进行。在死锁中,没有一个 goroutine 可以继续,永远
  • @mh-cbon:代码中有两个不同的渠道
  • 你不会“等待”死锁,它与 goroutine 的非活动时间无关多长时间
  • @HymnsForDisco 绝对是。这里的等待是经过深思熟虑的,我只想说明一点,不管主 go-routine 等待了多长时间,它仍然不是死锁的情况。
  • @EliBendersky 另外,2 个不同的频道不会有任何区别,IMO。

标签: go channels


【解决方案1】:

正如@icza 的评论正确指出的那样,当 所有 协程卡住并且无法取得进展时会发生死锁。在您的情况下,f1f2 被卡住了,但主 goroutine 没有 - 所以这不是死锁。

但是,它 goroutine 泄漏!当某些代码完成其逻辑存在但让 goroutine 运行(未终止)时,就会发生 Goroutine 泄漏。我发现像 github.com/fortytw2/leaktest 这样的工具可用于检测 goroutine 泄漏,它会检测代码中的问题 - 试一试。

这是一个修改后的代码示例:

import (
    "testing"
    "time"

    "github.com/fortytw2/leaktest"
)


func f1(done chan bool) {
    done <- true
    fmt.Printf("this's f1() goroutine\n")
}

func f2(done chan bool) {
    val := <-done
    fmt.Printf("this's f2() goroutine. val: %t\n", val)
}

func TestTwoGoroutines(t *testing.T) {
    defer leaktest.CheckTimeout(t, time.Second)()

    done1 := make(chan bool)
    done2 := make(chan bool)

    go f1(done1)
    go f2(done2)
}

当您运行此测试时,您会看到如下内容:

--- FAIL: TestTwoGoroutines (1.03s)
    leaktest.go:132: leaktest: timed out checking goroutines
    leaktest.go:150: leaktest: leaked goroutine: goroutine 7 [chan send]:
        leaktest-samples.f1(0xc000016420)
            leaktest-samples/leaktest1_test.go:45 +0x37
        created by leaktest-samples.TestTwoGoroutines
            leaktest-samples/leaktest1_test.go:60 +0xd1
    leaktest.go:150: leaktest: leaked goroutine: goroutine 8 [chan receive]:
        leaktest-samples.f2(0xc000016480)
            leaktest-samples/leaktest1_test.go:50 +0x3e
        created by leaktest-samples.TestTwoGoroutines
            leaktest-samples/leaktest1_test.go:61 +0xf3

【讨论】:

    【解决方案2】:

    正如@icza 所说,主 goroutine 可以继续并最终终止。

    如果您从两个函数调用中的任何一个中删除 go 关键字(使它们发生在主 goroutine 上),那么应用程序就会出现死锁。看这个去操场(它为你突出了僵局)https://play.golang.org/p/r9qo2sc9LQA

    【讨论】:

    • 但还有一件事。
    【解决方案3】:

    还有一件事。

    func f1(done chan bool) {
        fmt.Printf("f1()\n")
    }
    
    func main() {
        done := make(chan bool)
        go f1(done)
        done <- true
    }
    

    这里,调用者 go-routine 显然卡住了,因为不存在从通道接收的 go-routine。

    在这里,并不是所有的 go-routines 都被阻塞(f1() 滑过),但仍然存在死锁。原因是,必须至少有 1 个 goroutine 应该从通道接收。 但这显然与上面的评论相矛盾,并不是所有的 go-routines 都在这里被阻止。

    【讨论】:

    • f1 运行然后终止。因此只剩下一个 goroutine,主 goroutine - 被阻塞(等待接收),因此程序处于死锁状态。
    猜你喜欢
    • 2021-12-10
    • 1970-01-01
    • 1970-01-01
    • 2012-08-27
    • 1970-01-01
    • 2020-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多