2017-02-07 157 views
0

我收到「致命的錯誤:所有goroutines睡着了 - 死鎖! 」出於某種原因在下面的代碼。我使用應該是非阻塞的緩衝通道。不知道我做錯了頻道死亡在去

package main 

import (
    "fmt" 
    "sync" 
) 

func main() { 
    c := make(chan int, 2) 
    var wg sync.WaitGroup 
    wg.Add(2) 

    go doSomething(c, wg) 
    go doSomething(c, wg) 
    go doSomething(c, wg) 

    wg.Wait() 

    close(c) 

    for v := range c { 
     fmt.Print(v) 
    } 

} 

func doSomething(c chan<- int, wg sync.WaitGroup) { 
    defer wg.Done() 
    c <- 1 

} 

遊樂場鏈接https://play.golang.org/p/J9meD5aKna

回答

1

雖然你的解決方案可能工作,我不滿意它。

首先,您需要更改通道大小以使其工作的事實表明存在潛在的問題/錯誤。現在,每當您想要啓動另一個doSomething時,您都必須記住更改頻道的長度。

其次,你等到所有的goroutines完成之後才從通道讀取。這是一種「浪費」,因爲頻道範圍循環的一個要點是您不必等待所有項目都生成(寫入頻道),您可以儘快開始處理項目他們準備好了。

所以我會寫你的代碼,就像這樣

func main() { 
    c := make(chan int) 

    var wg sync.WaitGroup 
    wg.Add(3) 
    go func() { 
     doSomething(c) 
     defer wg.Done() 
    }() 
    go func() { 
     doSomething(c) 
     defer wg.Done() 
    }() 
    go func() { 
     doSomething(c) 
     defer wg.Done() 
    }() 

    go func() { 
     wg.Wait() 
     defer close(c) 
    }() 

    for v := range c { 
     fmt.Print(v) 
    } 
} 

func doSomething(c chan<- int) { 
    c <- 1 
} 

https://play.golang.org/p/T3dfiztKot

注意如何等待和關閉通道,現在是在它自己的夠程 - 這使得開始遍歷通道(其現在沒有緩衝!)馬上。

我也改變了代碼,以便WaitGroup從不離開它聲明的範圍(即它不被用作參數),這是我個人的偏好。我相信它使代碼更易於遵循和理解。

+0

我以爲在頻道關閉之前您無法對頻道進行排序。也許我錯了,但這就是它在這裏所說的http://guzalexander.com/2013/12/06/golang-channels-tutorial.html「正如上面提到的範圍將工作,直到通道明確關閉」。在這種情況下,我們需要等待另一個goroutine被執行。 – tabiul

+0

你誤會了 - 關鍵是循環不會結束,直到通道關閉。所以如果頻道從未關閉,你將會有無限循環(它會等待下一個項目或關閉頻道)。 – ain

+0

哦,那麼你的意思是,範圍將被阻止,直到頻道關閉。我認爲你的解決方案更好 – tabiul

0

我發現這個問題。實際上兩個問題

  1. 通道和工作組的大小應爲3

  2. 我應該通過WG作爲指針

更新代碼

package main 

import (
    "fmt" 
    "sync" 
) 

func main() { 
    c := make(chan int, 3) 
    var wg sync.WaitGroup 
    wg.Add(3) 

    go doSomething(c, &wg) 
    go doSomething(c, &wg) 
    go doSomething(c, &wg) 

    wg.Wait() 

    close(c) 

    for v := range c { 
     fmt.Print(v) 
    } 

} 

func doSomething(c chan<- int, wg *sync.WaitGroup) { 
    defer wg.Done() 
    c <- 1 

} 
0

是,有一個重要的問題問題在你的代碼。

你打電話go doSomething(c, wg)只是通過wg值。 你應該知道

Arguments in Go are always passed by value. Use a pointer when an argument may be modified.

所以你應該做這樣的go doSomething(c, &wg),然後wgmain功能將defer wg.Done()func doSomething(c chan<- int, wg sync.WaitGroup)進行修改。