2014-09-24 5 views
0

Я смотрю потрясающее видео о Advanced Go Concurrency Patterns. В начале Sameer Ajmani показывает приложение для пинг-понга.Получить тупик при попытке получить значение дважды

package main 

import (
    "fmt" 
    "time" 
) 

type Ball struct{ hits int } 

func main() { 
    table := make(chan *Ball) 
    go player("ping", table) 
    go player("pong", table) 

    table <- new(Ball) // game on; toss the ball 
    time.Sleep(1 * time.Second) 
    fmt.Println(<-table) // game over; grab the ball 
} 

func player(name string, table chan *Ball) { 
    for { 
     ball := <-table 
     ball.hits++ 
     fmt.Println(name, ball.hits) 
     time.Sleep(100 * time.Millisecond) 
     table <- ball 
    } 
} 

Код, как он работает, я понимаю до 90 процентов. Это два гортана, которые они посылают друг другу, пинг и понг, во время основного сна.

Тогда я пытаюсь следующий

package main 

import (
    "fmt" 
    "time" 
) 

type Ball struct{ hits int } 

func main() { 
    table := make(chan *Ball) 
    go player("ping", table) 
    go player("pong", table) 

    table <- new(Ball) // game on; toss the ball 
    time.Sleep(1 * time.Second) 
    fmt.Println(<-table) // game over; grab the ball 
    fmt.Println(<-table) // game over; grab the ball 
} 

func player(name string, table chan *Ball) { 
    for { 
     ball := <-table 
     ball.hits++ 
     fmt.Println(name, ball.hits) 
     time.Sleep(100 * time.Millisecond) 
     table <- ball 
    } 
} 

Я попал сюда в тупик, и на самом деле не понимаю, почему. Посмотрите на последнюю строку в рутине, я пытаюсь получить значение от канала, как вторая последняя строка. В фоновом режиме два goroutines продолжают цикл и посылают друг другу значение. Кажется, это для меня несколько приемников для табличного переменного канала.

Мой главный вопрос: что у меня есть у второго образца тупик?

+0

Не захватите мяч. Просто закройте канал. Чтобы проверить, что канал уже закрыт, используйте x, ok: = <-c notation. Пример http://play.golang.org/p/N579duQZOg – RoninDev

ответ

3

В фоновом режиме два goroutines продолжают цикл и посылают друг другу значение.

Нет, они этого не делают.

Когда вы делаете канал с make(chan *Ball), вы делаете небуферизованный канал. Это эквивалентно высказыванию make(chan *Ball,0), что означает, что канал может вместить в него 0 вещей - или, более явно, любая запись в канал будет блокироваться до тех пор, пока другая программа не прочитает канал, и наоборот.

Порядок исполнения с небуферизованным каналом заключается в следующем:

  • Игрок «пинг» создан, пытается прочитать из таблицы с ball := <-table, не блокируется до table записывается
  • Player «понг» создан, пытается прочитать из таблицы с ball := <-table, блокируется до тех пор, пока table записывается
  • Main пишет Болл в table с помощью следующей строки:

    table <- new(Ball) // game on; toss the ball 
    

    Это не заблокировано, потому что кто-то ждет, чтобы читать по каналу.

  • Теперь пинг читает мяч (выполнение продолжается после Пин ball := <-table)
  • Ping кладет мяч на стол с table <- ball, не заблокирован, потому что теннис ждет
  • Pong читает мяч (исполнение продолжается после понга-х ball := <-table)
  • Pong кладет мяч на стол с table <- ball, не заблокирован, потому что пинг ждет
  • Ping читает мяч (выполнение продолжается после того, как пинг-х ball := <-table)
  • ....и т.д.
    • Пока главный не заканчивает игру, читая мяч вместо одного из игроков

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

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

time.Sleep(1 * time.Second) 
fmt.Println(<-table) // game over; grab the ball 

После этого table канал будет пустым, и дальше читает на нем будет блокировать.

  • На данный момент как player процедуры заблокированы на ball := <- table.

Если вы еще <-table чтения в основном потоке, что чтение будет также блокировать, пока рутина пытается не писать в канал таблицы. Однако, поскольку никакие другие процедуры не выполняются, у вас есть тупик (все goroutines заблокированы).


ОК. Могу ли я просто поместить два шара в канал?

No.

Если попытаться поставить два мяча в канале, мы, вероятно, получим вывод:

Ping 1 
Pong 1 

, потому что оба player подпрограммы будет застрял, пытаясь поставить их мяч обратно в канал, но никто не будет читать его. Заказ выглядит следующим образом:

  • Игрок «пинг» создано, пытается прочитать из таблицы, заблокированные
  • Игрока «понг» создано, пытается прочитать из таблицы, заблокированные
  • главные ставят первый мяч в table (не заблокирован, потому что кто-то ждет, чтобы прочитать)
  • главный ставит второй мяч в table (не заблокирован, потому что кто-то ждет, чтобы прочитать)
  • Ping читает первый мяч
  • P Онг читает второй мяч
  • Ping ставит первый мяч на столе, заблокирован, потому что никто не ждет, чтобы читать
  • Pong ставит второй мяч на столе, заблокирован, потому что никто не ждет, чтобы читать
  • Оба игрока будут заблокированы
    • До главных концов игры, читая оба шара.

Here's a program to illustrate

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

+0

Когда я удаляю 'line table <- new (Ball) // игра on; бросить мяч, почему тогда у меня зашел тупик? –

+0

Опять же, у вас будет тупик, потому что все горуты ждут при чтении канала (но никто не собирается писать на канал). Я отредактировал свой ответ, надеюсь, сделать его более ясным. –

+0

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

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