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

すべてのゴルーチンが完了しますが、とにかくデッドロックがスローされます。タイマーが使用されているかどうかは問題ではなく、どちらの方法でもデッドロックが発生することに注意してください。

4

2 に答える 2

7

Goexitのドキュメントから:

Goexit は、それを呼び出したゴルーチンを終了します。他のゴルーチンは影響を受けません。Goexit は、ゴルーチンを終了する前にすべての遅延呼び出しを実行します。

メイン ルーチンを終了します。しないでください。実行すると、最後に起動したルーチンが終了した後に実行されるルーチンがないgo check(i)ため、「デッドロック」が発生します。この行を削除するだけです:

defer runtime.Goexit()

ゴルーチンのグループが終了するまでメインで待機したい場合は、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

すべてのゴルーチンは、誰かが送信した値を消費するのを待っています<-time.After。を削除する<-か、起動したすべてのゴルーチンの値をメインに消費させることができます。

編集

これは私のために働いた

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 に答える