Перейти каналы и тупик - PullRequest
13 голосов
/ 12 мая 2011

Я пытаюсь понять язык Го.Я попытался создать две процедуры, которые объединяют поток между ними, используя два канала:

func main() {
c1 := make(chan int)
c2 := make(chan int)

go func() {
    for i := range c1{
        println("G1 got", i)
        c2 <- i
    }
}()

go func() {
    for i := range c2 {
        println("G2 got", i)
        c1 <- i
    }
}()


c1 <- 1

time.Sleep(1000000000 * 50)
}

Как и ожидалось, этот код печатает:

 G1 got 1
 G2 got 1
 G1 got 1
 G2 got 1
 ....

Пока не завершится основная функция.

Но если я отправлю другое значение на один из каналов из основного, он внезапно блокирует:

func main() {
c1 := make(chan int)
c2 := make(chan int)

go func() {
    for i := range c1{
        println("G1 got", i)
        c2 <- i
    }
}()

go func() {
    for i := range c2 {
        println("G2 got", i)
        c1 <- i
    }
}()


c1 <- 1

time.Sleep(1000000000 * 1)

c1 <- 2

time.Sleep(1000000000 * 50)
}

Он выводит

G1 got 1
G2 got 1
G1 got 1
G2 got 1
G1 got 2

, а затем блокирует, пока основное не закончится.

Значение "2", отправленное на c1, поступает в первый goroutie, которое отправляет его в c2, но вторая программа никогда не получает.

(Использование буферизованных каналов с размером 1 (c1 или c2)работает в этом примере)

Почему это происходит?Когда это происходит в реальном коде, как я могу его отладить?

Ответы [ 2 ]

18 голосов
/ 13 мая 2011

nmichaels прав в своем ответе, но я подумал, что добавлю, что есть способы выяснить, где вы зашли в тупик при отладке такой проблемы.

Простой способ, если вы 'на Unix-подобной ОС, запустите команду

kill -6 [pid]

Это убьет программу и даст трассировку стека для каждой процедуры.

Несколько более сложный способ - присоединить gdb.

gdb [executable name] [pid]

Вы можете проверить стек и переменные активной программы как обычно, но не существует простого способа переключения программ, о которых я знаю.Вы можете переключать потоки ОС обычным способом, но этого может быть недостаточно, чтобы помочь.

18 голосов
/ 12 мая 2011

Каналы Go, созданные с помощью make(chan int), не буферизуются. Если вы хотите буферизованный канал (который не обязательно блокируется), сделайте его с make(chan int, 2), где 2 - размер канала.

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

Причина, по которой он заходит в тупик, состоит в том, что ваша первая программа ожидает завершения c2 <- i, в то время как вторая ожидает завершения c1 <- i, потому что в c1 была дополнительная вещь. Лучший способ отладить такого рода вещи, когда это происходит в реальном коде, - это посмотреть, какие программы заблокированы, и хорошо подумать.

Вы также можете обойти проблему, используя только синхронные каналы, если они действительно необходимы.

...