web-dev-qa-db-ja.com

すべてのゴルーチンが眠っています-デッドロック

私の要件の1つとして、N個のworker goルーチンを作成する必要があります。これは、1つの監視ルーチンによって監視されます。監視ルーチンは、すべてのワーカールーチンが完了したときに終了する必要があります。デッドロックで終わる私のコード、助けてください。

import "fmt"
import "sync"
import "strconv"

func worker(wg *sync.WaitGroup, cs chan string, i int ){
    defer wg.Done()
    cs<-"worker"+strconv.Itoa(i)    
}

func monitorWorker(wg *sync.WaitGroup, cs chan string) {
    defer wg.Done()
    for i:= range cs {
            fmt.Println(i)
     }
}
func main() {
    wg := &sync.WaitGroup{}
    cs := make(chan string)

    for i:=0;i<10;i++{
             wg.Add(1)
             go worker(wg,cs,i)
    } 

    wg.Add(1)
    go monitorWorker(wg,cs)
    wg.Wait()
}
18
vrbilgi

あなたのmonitorWorkerは決して死ぬことはありません。すべてのワーカーが終了すると、csを待機し続けます。これは、他にcsで送信されないため、wgが0に到達しないため、デッドロックになります。考えられる修正は、すべてのワーカーが終了したときにモニターにチャネルを閉じさせることです。 forループがメインにある場合、ループを終了し、メインから戻り、プログラムを終了します。

例: http://play.golang.org/p/nai7XtTMfr

package main

import (
    "fmt"
    "strconv"
    "sync"
)

func worker(wg *sync.WaitGroup, cs chan string, i int) {
    defer wg.Done()
    cs <- "worker" + strconv.Itoa(i)
}

func monitorWorker(wg *sync.WaitGroup, cs chan string) {
    wg.Wait()
    close(cs)
}

func main() {
    wg := &sync.WaitGroup{}
    cs := make(chan string)

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go worker(wg, cs, i)
    }

    go monitorWorker(wg, cs)

    for i := range cs {
        fmt.Println(i)

    }
}

編集:これはOPの最初のコメントに対する回答です。

プログラムには、同期する必要のある3つの部分があります。まず、すべてのワーカーがデータを送信する必要があります。次に、印刷ループはそのデータを印刷する必要があります。次に、メイン関数が戻る必要があり、それによってプログラムが終了します。あなたの例では、すべてのワーカーがデータを送信し、すべてのデータが出力されますが、メッセージがメインに送信されることはなく、正常に返される必要があります。

私の例では、mainが印刷を行い、「monitorWorker」は、印刷する必要のあるすべてのデータを受信したときにmainに通知するだけです。このようにして、プログラムはデッドロックではなく正常に終了します。

印刷ループが別のゴルーチンにあることを主張する場合は、それを行うことができます。ただし、メインに戻るように、追加の通信をメインに送信する必要があります。この次の例では、チャネルを使用して、すべてのデータが印刷されるときにメインエンドを確保します。

package main

import (
    "fmt"
    "strconv"
    "sync"
)

func worker(wg *sync.WaitGroup, cs chan string, i int) {
    defer wg.Done()
    cs <- "worker" + strconv.Itoa(i)
}

func monitorWorker(wg *sync.WaitGroup, cs chan string) {
    wg.Wait()
    close(cs)
}

func printWorker(cs <-chan string, done chan<- bool) {
    for i := range cs {
        fmt.Println(i)
    }

    done <- true
}

func main() {
    wg := &sync.WaitGroup{}
    cs := make(chan string)

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go worker(wg, cs, i)
    }

    go monitorWorker(wg, cs)

    done := make(chan bool, 1)
    go printWorker(cs, done)
    <-done
}
21

チャネルが受信するメッセージの数がわかっている場合は、ループを制限するだけです。

//c is channel
for a := 1; a <= 3; a++{
        fmt.Println(<-c)
}

また、別のチャネル(ワーカーのステータス)をワーカーに渡して、デッドロックの原因となるループを条件付きで停止することもできます。

追伸:それは単なる追加の迅速な解決策です。特にソリューションに対応しているわけではありません。

0
serkan