Проблема параллелизма в понимании примера из электронной книги - PullRequest
0 голосов
/ 03 июня 2018

Мне трудно понять параллелизм из электронной книги.Я надеюсь, что кто-то может сделать шаг за шагом со мной, чтобы я мог ясно понять, что происходит.

Вот основной метод:

func main(){
  c := make(chan int)
  go printer(c)
  wg.Add(1)

  // Send 10 integers on the channel.
  for i := 1; i <= 10; i++ {
    c <- i
  }

  close(c)
  wg.Wait()
}

А вот метод принтера:

func printer(ch chan int) {
    for i := range ch {
        fmt.Printf("Received %d ", i)
    }
    wg.Done()
}

Вот мои вопросы:

  • Почему мы выполняем wg.Add(1) только как одну группу, которую нужно ждать вместо wg.Add(1) внутри main method for loop
  • Я действительно не разбираюсь в каналах, точка.

Я провел какое-то исследование, и, похоже, никто не может объяснить мне это простым способом.

Любое пошаговое простое объяснение sending integers on channel и adding to wait groups приветствуется.

ОБНОВЛЕНИЕ

исходный код => https://github.com/goinaction/code/blob/master/chapter1/channels/hellochannels.go

Ответы [ 2 ]

0 голосов
/ 03 июня 2018

Если вы намеренно не добавите точку блокировки в основной поток, программа остановит выполнение до того, как получатель напечатает последний элемент.Это работа, выполняемая объектом WaitGroup и вызовом WaitGroup.Wait().

При этом вы сообщаете основному потоку, что одновременно выполняется другая операция и что вы хотите дождаться ее завершения, прежде чем завершить выполнение вашей программы.Об этом будет сообщено с помощью оператора wg.Done().

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

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

Поскольку в данном примере мы работаем с каналом Unbuffered, он позволяет передавать по каналу только 1 сообщение за один раз. Это означает, что для каждого сообщения, отправленного передатчиком, передатчик будет заблокирован до тех пор, пока получатель не извлечет такое сообщение и не удалит его из канала.

Когда передатчик отправит последний элемент,он закроет канал, указывая получателю, что больше сообщений не будет приниматься позже.Итерация, происходящая с range ch, прекратит работу после получения «10», и выполнение goroutine окончательно прервет выполнение цикла.

Вот что происходит:

// Передатчик отправляет "1"

// Передатчик теперь заблокирован, так как канал unbuffered.

// Receiver получает "1"

// Receiver теперь блокируется, пока что-то не передается на канал.

// Передатчик теперь разблокирован и может передавать снова.

// .....

// Передатчик отправляет "10"

// Приемник получил "10"

После этого функция goможет, наконец, вычесть счетчик на WaitGroup, вызвав его статический метод Done, и счетчик достигнет нуля, что означает, что операция блока, происходящая из-за wg.Wait(), прекратит свое существование, и основной поток может наконец достичьзавершение и прекращение выполнения программы.

Ключевые моменты:

  • channel, который мы используем для канала unbuffered, что означает 1 сообщение за раз.С другой стороны, с буферизованными каналами вы можете одновременно передавать несколько сообщений, прежде чем получатель действительно решит их получить, и преимущества WaitGroup могут стать намного более очевидными.
  • с <- i, вы отправляете что-то на channel, а с range ch вы получаете такие сообщения.Передаваемые сообщения имеют фиксированный тип переменной, в данном случае целое число.
0 голосов
/ 03 июня 2018

Примечание редактора: этот первый абзац был написан в ответ на оригинальный код автора , который с тех пор отредактирован так, что его семантика заметно отличается от кода, который был первоначально представлен.

Пример кода, который вы предоставили, не очень хороший пример, поскольку демонстрирует использование WaitGroup, потому что программа завершается в конце основного метода, а не в какой-либо координации, обрабатываемойWaitGroup.Вы можете удалить вызов на wg.Done(), и эта программа все равно будет компилироваться и выполняться нормально.

Хотя я с удовольствием отвечу на ваши более общие вопросы.

Почему мы выполняем только wg.Add (1) как одну группу ожидания вместо wg.Add (1) внутри основного метода для цикла?

Похоже, вы спрашиваете, почему мы не увеличиваем wg для каждого целого числа в цикле for.Это не совсем точка WaitGroups.Типичным вариантом использования будет добавление 1 для каждой программы, которую мы координируем за пределами основной программы.В этом примере есть еще одна goroutine (которая обрабатывает printer, поэтому мы добавляем только 1 к нашей WaitGroup.

Более clear версии вашего приведенного выше примера будет что-тонапример:

var wg sync.WaitGroup

func printer(ch chan int) {
    for i := range ch {
        fmt.Printf("Received %d ", i)
    }
    wg.Done()
}

func sender(ch chan int) {
    for i := 0; i <= 10; i++ {
        ch <- i
    }
    close(ch)
}

func main() {
    c := make(chan int)
    go printer(c)
    go sender(c)
    wg.Add(1)

    fmt.Println("Waiting...")
    wg.Wait()
    fmt.Println("Finished")
}

В этом примере мы хотим ждать , чтобы метод printer полностью распечатал все его числа, мы переходим к основной процедуре.мы сообщаем нашей основной программе Wait() до тех пор, пока все целые числа не будут напечатаны, и в этот момент мы говорим основному потоку продолжить. В отличие от вашего примера, мы close канал, как только мы ввели в него все наши целые, чтопозволяет функции printer прекратить блокировку на range и продолжить и вызвать wg.Done - в вашем исходном примере printer никогда не прекращает блокировку на range и поэтому фактически никогда не вызывает wg.Done

Я действительно не понимаю каналы, точка.

Надеюсь, приведенный выше пример проясняет ситуацию? Боюсь, эта часть вашего вопроса слишком широкадля меня то ответ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...