2015-12-31 2 views
1

Я пытаюсь иметь две отдельные подпрограммы для пользователей, которые будут фильтровать четные и нечетные числа из входного канала. Это всего лишь пример игрушки, чтобы увидеть, есть ли у потребителей возможность что-то сделать с сообщением, считываемым из входного канала, если оно соответствует определенному условию, в противном случае возвращается обратно на входной канал.«Потребляйте или возвращайте» каналы «Go»

Мой текущий код выглядит следующим образом:

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 

Ссылка на Go Playground: https://play.golang.org/p/9RIvFsGKI-

+0

@ ZanLynx: в то время как этот вопрос говорит, что они имеют тупик в своем образце кода, похоже, что они пытаются сделать то же самое, что и в этом другом вопросе. –

+0

@JamesHenstridge Конечно, не то же самое. Но похоже, что общая проблема и решение для этого вопроса одинаковы. –

+0

И я заметил, что даже если вы исправите буферизацию, ваши циклы for не находятся в goroutines и, следовательно, будут блокироваться, если вы никогда не делаете свои буферы большими, чем размер вашего цикла. –

ответ

2

У вас есть тупик, потому что ваш четные и нечетные goroutines блокируются при отправке на out, потому что от него ничего не читается. Почему ничего не читает out? Потому чтоgoroutine заблокирован при отправке на input, потому что от него ничего не читается. Почему ничего не читается от input? Потому что два гортани, которые будут читать из него, блокируются.

Кроме того, как filterEven и filterOdd будет работать только один раз, если вы не обернуть их содержимое в for { } (но тогда они никогда не остановятся, пока вас break). С другой стороны, range even будет блокировать (и range odd никогда не произойдет), когда ничего не остается писать до even, потому что range по каналу останавливается только при закрытии канала или вызывается break.

В общем, это не сложно решить, если вы знаете, когда вы можете закрыть канал. С тем, что вы описываете, это становится сложнее. Ни один из goroutines не знает, когда это нормально, чтобы закрыть input, потому что все три пишут ему, а два тоже читают. Вы можете использовать sync.WaitGroup, чтобы убедиться, что все, что вы положили в канал input, было обработано до его закрытия. Как только он будет закрыт, два других гортана могут использовать это как сигнал для закрытия своих собственных каналов и break или return для завершения работы.

Однако записи в каналы in и out по-прежнему будут блокироваться до тех пор, пока не будет прочитано соответствующее сообщение, поскольку они не загружены. Однако, если вы их буферизируете, указав размер как второй аргумент на make, записи не будут блокироваться до тех пор, пока канал не будет заполнен. Так как вы не знаете ни even, ни odd будут иметь больше написанных для них, чем то, что main пришлите по адресу input, вы можете использовать это как безопасную емкость буфера.

Вот пример использования WaitGroup с буферизацией каналов для вашего кода: https://play.golang.org/p/VXqfwUwRcx

Если вы не хотите буферизацией каналов, вы можете также использовать другую пару goroutines, чтобы захватить значения и отправить их обратно в main как только что закончилось.Таким образом, пишет на even и odd каналов не блокируют: https://play.golang.org/p/i5vLDcsK1v

В противном случае, если не нужно печатать содержимое каждого канала одновременно, вы можете использовать эти два дополнительных goroutines для чтения из каналов и напечатайте сразу: https://play.golang.org/p/OCaUTcJkKB

Смежные вопросы