为什么这个Go代码会死锁?

sai*_*int 2 concurrency deadlock go goroutine

package main

import "fmt"
import "runtime"
import "time"


func check(id int) {
    fmt.Println("Checked", id)
    <-time.After(time.Duration(id)*time.Millisecond)
    fmt.Println("Woke up", id)
}

func main() {
    defer runtime.Goexit()

    for i := 0; i <= 10; i++ {
        fmt.Println("Called with", i)
        go check(i)
    }

    fmt.Println("Done for")
}
Run Code Online (Sandbox Code Playgroud)

我是Go的新手,所以任何指针都会很棒.我怎么去调试这样的东西?

您可以运行代码段http://play.golang.org/p/SCr8TZXQUE

更新:这<-time.After(time.Duration(id)*time.Millisecond)在游乐场没有线路的情况下工作 ,我想知道为什么?(正如@dystroy所说,这可能是因为游乐场处理时间的方式)

当我在本地尝试时,这是输出:

Called with  0
Called with  1
Checked 0
Called with  2
Checked 1
Called with  3
Checked 2
Called with  4
Woke up 0
Checked 3
Called with  5
Checked 4
Called with  6
Checked 5
Called with  7
Checked 6
Called with  8
Checked 7
Called with  9
Checked 8
Called with  10
Checked 9
Woke up 1
Done for
Checked 10
Woke up 2
Woke up 3
Woke up 4
Woke up 5
Woke up 6
Woke up 7
Woke up 8
Woke up 9
Woke up 10
throw: all goroutines are asleep - deadlock!

goroutine 2 [syscall]:
created by runtime.main
    /tmp/bindist046461602/go/src/pkg/runtime/proc.c:221

goroutine 5 [timer goroutine (idle)]:
created by addtimer
    /tmp/bindist046461602/go/src/pkg/runtime/ztime_amd64.c:69
exit status 2
Run Code Online (Sandbox Code Playgroud)

所有的goroutine都完成但无论如何都会陷入僵局.我应该注意,如果使用定时器无关紧要,无论如何都会死锁.

Den*_*ret 7

Goexit的文档:

Goexit终止调用它的goroutine.没有其他goroutine受到影响.Goexit在终止goroutine之前运行所有延迟调用.

你正在退出主程序.别.正如您所做的那样,在您启动的最后一个go check(i)完成之后,没有任何例程运行,因此"死锁".只需删除此行:

defer runtime.Goexit()
Run Code Online (Sandbox Code Playgroud)

如果你想要的是主要等待一组goroutine完成,你可以使用sync.WaitGroup:

package main

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

func check(id int, wg *sync.WaitGroup) {
    fmt.Println("Checked", id)
    <-time.After(time.Duration(id)*time.Millisecond)
    fmt.Println("Woke up", id)
    wg.Done()
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i <= 10; i++ {
        wg.Add(1)
        fmt.Println("Called with", i)
        go check(i, &wg)
    }
    wg.Wait()
    fmt.Println("Done for")
}
Run Code Online (Sandbox Code Playgroud)

编辑:

如果你在golang的游乐场进行测试,任何人time.After都会陷入僵局,因为时间在游乐场被冻结,Goexit可能会退出一个标准程序中甚至不存在的例程.

  • 使用`runtime.Goexit()`我正在等待其他goroutine完成,它似乎工作.我的意思是所有goroutines都运行`fmt.Println("Woke up")`但是它无论如何都会检测到死锁.没有那条线,它不会等待其他goroutines完成. (2认同)