2017-05-29 6 views
2

私は1000人のワーカーのワーキングプールを作成してチャネルで遊んでいます。現在、私は次のエラーを取得しています:ワーキングプールのチャネルのデッドロック

ここ
fatal error: all goroutines are asleep - deadlock! 

私のコードです:

package main 

import "fmt" 
import "time" 


func worker(id int, jobs <-chan int, results chan<- int) { 
    for j := range jobs { 
     fmt.Println("worker", id, "started job", j) 
     time.Sleep(time.Second) 
     fmt.Println("worker", id, "finished job", j) 
     results <- j * 2 
    } 
} 

func main() { 
    jobs := make(chan int, 100) 
    results := make(chan int, 100) 

    for w := 1; w <= 1000; w++ { 
     go worker(w, jobs, results) 
    } 

    for j := 1; j < 1000000; j++ { 
     jobs <- j 
    } 
    close(jobs) 
    fmt.Println("==========CLOSED==============") 

    for i:=0;i<len(results);i++ { 
     <-results 
    } 
} 

ですが、なぜでしょうか?私はまだ新しいです、そして、私はこれを理解することを望んでいます。

答えて

1

問題は、チャンネルがいっぱいになっていることです。 main()ルーチンは、すべての結果を読み取る前にすべてのジョブをjobsチャネルに入れようとします。しかし、resultsチャンネルは、チャンネルへの書き込みがブロックされる前に、100個の結果のためのスペースしかありません。したがって、すべてのワーカーは、resultsからまだ読み取られていないので、このチャンネルのスペースを待つことをブロックします。

これをすばやく修正するには、jobsをすべてのジョブを保持するのに十分な大きさにすると、main()ファンクションは読み取りフェーズに進むことができます。またはすべての結果を保持するのに十分な大きさのresultsを作ることができます。その結果、ワーカーはブロックせずに結果を出力できます。

よりよいアプローチはとてもmain()は、読み取り結果にまっすぐに行くことができ、jobsキューを埋めるために別のゴルーチンを作ることです:私は固定数に最終forループを変更する必要がありました

func main() { 
    jobs := make(chan int, 100) 
    results := make(chan int, 100) 

    for w := 1; w <= 1000; w++ { 
     go worker(w, jobs, results) 
    } 

    go func() { 
     for j := 1; j < 1000000; j++ { 
      jobs <- j 
     } 
     close(jobs) 
     fmt.Println("==========CLOSED==============") 
    } 

    for i := 1; i < 1000000; i++ { 
     <-results 
    } 
} 

注意それ以外の場合は、すべての結果が読み込まれる前に終了することがあります。

+0

をバッファー? – rhillhouse

+0

チャネル・サイズを指定しないと、バッファ・サイズが0になります。つまり、チャネルへの書き込みは、リーダが使用可能になるまでブロックされます。無制限のサイズのチャネルを設計で作成することはできません(これは、たとえばサーバーでの制約のないメモリ使用につながる可能性があるためです)。 – Thomas

1

次のコード:すべての労働者がメインゴルーチンがループに陥っている間に、結果チャネルで受信するメインgorourineを待ってブロックされますので、

for j := 1; j < 1000000; j++ { 
     jobs <- j 
    } 

は、別のゴルーチンで実行する必要があります。

2

トーマスの答えは基本的に正しいですが、私はIMOよりよい行くであり、また、バッファなしのチャネルで動作します私のバージョン投稿:私は削除した場合、なぜそれがまだ起こらない現在、両方のチャネルが100でバッファリングされている

func main() { 
    jobs := make(chan int) 
    results := make(chan int) 

    var wg sync.WaitGroup 

    // you could init the WaitGroup's count here with one call but this is error 
    // prone - if you change the loop's size you could forget to change the 
    // WG's count. So call wg.Add in loop 
    //wg.Add(1000) 
    for w := 1; w <= 1000; w++ { 
     wg.Add(1) 
     go func() { 
      worker(w, jobs, results) 
      defer wg.Done() 
     }() 
    } 

    go func() { 
     for j := 1; j < 2000; j++ { 
      jobs <- j 
     } 
     close(jobs) 
     fmt.Println("==========CLOSED==============") 
    }() 

    // in this gorutine we wait until all "producer" routines are done 
    // then close the results channel so that the consumer loop stops 
    go func() { 
     wg.Wait() 
     close(results) 
    }() 

    for i := range results { 
     fmt.Print(i, " ") 
    } 
    fmt.Println("==========DONE==============") 
} 
関連する問題