2017-05-29 63 views
2

我正在通過製作1000名工作人員的工作區來播放頻道。目前,我收到以下錯誤:Workerpool上的頻道死鎖

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個結果的空間才能阻止頻道的寫入,因此所有工作人員最終都會阻止在此頻道中等待空間 - 因爲main()尚未開始從results開始讀取,所以永遠不會出現空間。

要快速解決此問題,您可以使jobs大到足以容納所有作業,以便main()函數可以繼續讀取階段;或者您可以讓results大到足以保存所有結果,這樣工作人員就可以輸出結果而不會阻塞。

一個更好的方法是讓另一個夠程,填補了jobs隊列,所以main()可以直接去讀取結果:

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 
    } 
} 

注意,我不得不最終for循環更改爲固定數量的迭代,否則它可能會在所有結果被讀取之前終止。

+0

目前兩個通道都在100緩衝爲何仍發生,如果我刪除緩衝區? – rhillhouse

+0

如果您未指定通道大小,則會得到0的緩衝區大小,這意味着寫入通道的通道將阻塞,直到讀取器可用。您無法通過設計創建無限大小的頻道(例如,這可能會導致服務器內存使用量不受限制)。 – Thomas

1

以下代碼:

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

應在一個單獨的goroutine運行,因爲所有的工人將阻塞等待主gorourine接收結果信道上,而主夠程卡在循環。

2

雖然托馬斯的回答基本上是正確的,我張貼我的版本,這是IMO最好去也無緩衝通道工程:

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