2
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")
}

我对 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

所有的 goroutine 都完成了,但无论如何都会引发死锁。我应该注意,如果使用计时器并不重要,无论哪种方式都会死锁。

4

2 回答 2

7

Goexit 的文档中

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

您正在退出主程序。不。当您这样做时,在您启动的最后一个程序完成后没有任何例程运行go check(i),因此出现“死锁”。只需删除此行:

defer runtime.Goexit()

如果您想要在 main 中等待一组 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")
}

编辑 :

如果你在 golang 的操场上测试它,任何人time.After都会死锁,因为操场上的时间被冻结了,Goexit 可能会退出一个标准程序中甚至不存在的例程。

于 2012-10-05T12:01:44.190 回答
0

您所有的 goroutine 都在等待某人使用他们发送的值<-time.After。您可以删除<-或使 main 消耗您启动的所有 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")
}

女巫与之前有人提出的解决方案相同,所以我将提出一个没有等待组的解决方案

package main

import "fmt"
import "time"


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

func main() {
    c := make(chan bool)

    for i := 0; i <= 10; i++ {
        fmt.Println("Called with", i)
        go check(i, c)
    }
    var n uint
    for n<10 {
        <- c
        n++
    }
    fmt.Println("Done for")
}
于 2012-10-05T16:56:23.050 回答