2015-12-31 79 views
1

我想有兩個單獨的消費者去例程,將來自輸入通道篩選出偶數和奇數。這只是一個玩具的例子,以便看看消費者是否有可能通過從輸入通道讀取的消息做某些事情(如果它符合某些條件的話),否則放回到輸入通道。「消費或放回」去渠道

我當前的代碼如下:

package main 

func filterOdd(ch chan int, out chan int) { 
    val := <- ch 
    if val % 2 == 0 { 
     ch <- val 
    } else { 
     out <- val 
    } 
} 
func filterEven(ch chan int, out chan int) { 
    val := <- ch 
    if val % 2 != 0 { 
     ch <- val 
    } else { 
     out <- val 
    } 
} 

func main() { 
    even := make(chan int) 
    odd := make(chan int) 
    input := make(chan int) 
    go filterOdd(input, odd) 
    go filterEven(input, even) 
    for i:=1; i <= 10; i++ { 
     input <- i 
    } 

    println("Even...") 
    for i := range even { 
     println(i) 
    } 

    println("Odd...") 
    for i := range odd { 
     println(i) 
    } 
} 

然而,這將產生以下的輸出:

fatal error: all goroutines are asleep - deadlock! 

goroutine 1 [chan send]: 
main.main() 
    /tmp/sandbox594577124/main.go:27 +0x140 

goroutine 4 [chan send]: 
main.filterOdd(0x10336100, 0x103360c0) 
    /tmp/sandbox594577124/main.go:8 +0xc0 
created by main.main 
    /tmp/sandbox594577124/main.go:24 +0xc0 

鏈接到去遊樂場:https://play.golang.org/p/9RIvFsGKI-

+0

@ZanLynx:雖然這個問題說他們在示例代碼中遇到了一個死鎖,但聽起來他們聽起來並不像在其他問題中所做的那樣。 –

+0

@JamesHenstridge當然不是一回事。但它看起來像這個問題的一般問題和解決方案是相同的。 –

+0

我只注意到,即使你修復了緩衝區,你的for循環不在goroutine中,因此會阻塞,除非你總是使緩衝區大於循環的大小。 –

回答

2

你有一個僵局,因爲你發送到out,因爲沒有任何內容正在讀取,因此偶數和古怪的goroutines被阻止。爲什麼沒有讀out?因爲main goroutine在發送到input時被阻止,因爲沒有任何內容正在讀取它。爲什麼沒有從input讀取任何內容?因爲從它讀取的兩個goroutine被阻塞。

而且,無論是filterEvenfilterOdd,除非你在包裝的for { }內容將只運行一次(但他們永遠不會停止,直到你break)。在另一方面,range even將阻止(和range odd從未發生過),當什麼也沒留下寫even,因爲range在通道僅當通道被關閉或break被稱爲停止。

一般情況下,這些都不是難題,只要你知道你什麼時候可以關閉一個通道來解決。隨着你所描述的,這變得更加困難。沒有任何一家公司知道什麼時候可以關閉input,因爲所有三家公司都向其寫入,兩家公司也從中讀取。您可以使用sync.WaitGroup以確保您在關閉之前處理的所有內容都已處理完畢。一旦它的關閉,其他兩個夠程可以使用它作爲一個信號,關閉自己的渠道和breakreturn完成運行。

但是,寫入inout通道仍會阻塞,直到出現相應的讀取爲止,因爲它們是無緩衝的。但是,如果通過指定大小作爲make的第二個參數來緩衝它們,則寫入將不會阻塞,直到通道已滿。既然你既不知道evenodd將有更多的寫入他們比什麼main發送到input,你可以使用它作爲安全緩衝能力。

下面是一個使用WaitGroup與你的代碼緩存頻道的例子:https://play.golang.org/p/VXqfwUwRcx

如果你不想緩衝通道,您也可以使用另一對夠程的捕獲值,並送他們回main作爲切片一旦完成。這種方式寫在evenodd渠道不會阻塞:https://play.golang.org/p/i5vLDcsK1v

否則,如果不需要一次全部打印每個通道的內容,你可以使用這些兩個額外的夠程從通道讀取和馬上打印:https://play.golang.org/p/OCaUTcJkKB