2017-01-20 13 views
0

並列のゴルーチン間でチャネルを共有する方法を教えるためにこのサンプルコードを書いており、競合状態に遭っています。プログラムは、システム上に利用可能なCPUがあるように多くのゴルーチンを起動する必要があります。 blチャネルにアクセスする最初のゴルーチンは、チャネルをfalseにするように設定し、他のゴルーチンがstチャネル上のループにアクセスできないようにします。他のゴルーチンは、blチャネルにアクセスしてstチャネルから読み込み、各値を出力する最初のゴルーチンとして終了する必要があります。私は、コードを実行すると並列ゴルーチン間でチャネルを共有するときのGolang競合状態

package main 

import (
    "fmt" 
    "runtime" 
    "strconv" 
    "math/rand" 
    "time" 
    "sync" 
) 

func main() { 
    runtime.GOMAXPROCS(runtime.NumCPU()) 
    var wg sync.WaitGroup 
    st := make(chan string) 
    bl := make(chan bool) 
    go func() {bl <- true}() 

    for i := 0; i < runtime.NumCPU(); i++ { 
     wg.Add(1) 
     go func(x int) { 
      defer wg.Done() 
      fmt.Println("me: ", strconv.Itoa(x)) 

      can := <- bl 
      bl <- false 

      if can { 
       for val := range st { 
        t  := strconv.Itoa(rand.Int()%3)+"s" 
        dur, _ := time.ParseDuration(t) 
        time.Sleep(dur) 

         fmt.Println("time: ", t," i: ", strconv.Itoa(x), val) 
       } 
      } 
      fmt.Println("done: ", strconv.Itoa(x)) 
     }(i) 
    } 

    for i := 0; i < 10; i++ { 
     st <- "n: "+strconv.Itoa(i) 
    } 

    wg.Wait() 
    close(st) 
} 

、私は次のエラーを取得する:

$ go run share.go 
me: 1 
me: 0 
me: 2 
me: 3 
done: 0 
done: 2 
time: 2s i: 1 n: 0 
time: 1s i: 1 n: 1 
time: 0s i: 1 n: 2 
time: 2s i: 1 n: 3 
time: 2s i: 1 n: 4 
time: 2s i: 1 n: 5 
time: 0s i: 1 n: 6 
time: 2s i: 1 n: 7 
time: 2s i: 1 n: 8 
time: 2s i: 1 n: 9 
fatal error: all goroutines are asleep - deadlock! 

goroutine 1 [semacquire]: 
sync.runtime_Semacquire(0xc4200701bc) 
    /usr/local/go/src/runtime/sema.go:47 +0x30 
sync.(*WaitGroup).Wait(0xc4200701b0) 
    /usr/local/go/src/sync/waitgroup.go:131 +0x97 
main.main() 
    /share.go:80 +0x1ef 

goroutine 36 [chan receive]: 
main.main.func2(0xc4200701b0, 0xc42006e0c0, 0xc42006e060, 0x1) 
    /share.go:64 +0x23e 
created by main.main 
    /share.go:73 +0x127 

goroutine 38 [chan send]: 
main.main.func2(0xc4200701b0, 0xc42006e0c0, 0xc42006e060, 0x3) 
    /share.go:61 +0x1ef 
created by main.main 
    /share.go:73 +0x127 
exit status 2 

は私がblチャネルのためのこのレースの条件を処理するかどうかはわかりません。最後のgoroutineはblチャンネルからの読み込みを試みているようですが、それ以前のgoroutineにはまだfalseが挿入されていないため、何も読み込めません。私の直感が正しいかどうかは分かりません。私はコードの周りに印字行を置いたが、それは少なくとも起こっているようだ。私もblチャネルの周りにifを置こうとしましたが、それは同じ問題で終わった。私もbl <- falseif can {ブロック内に移動しましたが、それも機能しませんでした。これをどうやって解決するのですか?

答えて

1

チャネル上でチャネルを送信して、選択した値の単一アクセスを保護することができます。受信者だけが作業を行い、他の人は終了信号を受信したときに離れることになります。 this solution

+0

現在の解決策の問題は、b1チャンネルを送信する最後のゴルーチンが受信者が送信された値を取得するのを待つため、ゴルーチンが決して終了せず、全体を待っているメインのゴルーチンゴルーチンのグループ1がチャネルのバッファサイズである場合、バッファ付きチャネル(b1:= make(chan bool、1))を使用して固定できます。これはあなたのアプローチがちょうどうまくいくようにしますが、私の提案する解決策は、値がチャネルを介して送信される理由をより明示的だと思います。 –

+0

恐ろしいです!この例をありがとう。それが本当に助けになりました。 – Soubriquet

関連する問題