другой Голанг направляет вопросы о понимании того, как это происходит - PullRequest
0 голосов
/ 05 ноября 2019

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

package main

import (
    "fmt"
)

func main() {
    n := 3
    in := make(chan int)
    out := make(chan int)

    // We now supply 2 channels to the `multiplyByTwo` function
    // One for sending data and one for receiving
    go multiplyByTwo(in, out)

    // We then send it data through the channel and wait for the result
    in <- n
    in <- 3
    in <- 6
    in <- 10
    fmt.Println(<-out)
}

func multiplyByTwo(in <-chan int, out chan<- int) {
    // This line is just to illustrate that there is code that is
    // executed before we have to wait on the `in` channel
    fmt.Println("Initializing goroutine...")

    // The goroutine does not proceed until data is received on the `in` channel
    num := <-in

    // The rest is unchanged
    result := num * 2
    out <- result
}

, но это выдает ошибку:

Initializing goroutine...
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
    /tmp/sandbox639017164/prog.go:18 +0xe0

goroutine 6 [chan send]:
main.multiplyByTwo(0x430080, 0x4300c0)
    /tmp/sandbox639017164/prog.go:34 +0xe0
created by main.main
    /tmp/sandbox639017164/prog.go:14 +0xa0

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

, если я запусту его без этого канала:

package main

import (
    "fmt"
)

func main() {
    n := 3
    in := make(chan int)
    //out := make(chan int)

    // We now supply 2 channels to the `multiplyByTwo` function
    // One for sending data and one for receiving
    go multiplyByTwo(in)

    // We then send it data through the channel and wait for the result
    in <- n
    in <- 3
    in <- 6
    in <- 10
}

func multiplyByTwo(in <-chan int) {
    // This line is just to illustrate that there is code that is
    // executed before we have to wait on the `in` channel
    fmt.Println("Initializing goroutine...")

    // The goroutine does not proceed until data is received on the `in` channel
    num := <-in

    // The rest is unchanged
    result := num * 2
    fmt.Println(result)
}

он обработаетпервый вход в канал, но затем снова выводятся ошибки. fatal error: all goroutines are asleep - deadlock!

Ответы [ 2 ]

3 голосов
/ 05 ноября 2019

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

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

Это будет работать:

func multiplyByTwo(in <-chan int) {
    for num:=range in {
       // process num
    }
    // If here, then channel in is closed
}

in <- n
in <- 3
in <- 6
in <- 10
close(in)
// Wait for the goroutine to finish
1 голос
/ 06 ноября 2019

Вы можете, если хотите, рассматривать канал как своего рода почтовый ящик (возможно, со специальными возможностями телепортации, например, портал из игрового портала ).

An unbuffered канал - это почтовый ящик, в котором вообще нет места для каких-либо пакетов. Чтобы кто-то отправил посылку по почте (отправил значение), он должен дождаться, пока рука получателя не высунется из почтового ящика. Затем они могут бросить посылку в руку, которая вернется обратно в почтовый ящик, забрав посылку с собой. Если кто-то в очереди, вы должны встать в очередь за кем-то еще.

A буферизованный канал - это почтовый ящик, который может содержать один или несколько пакетов. Чтобы отправить посылку, зайдите в линию, если она есть. Когда вы достигнете головы линии, вы можете посмотреть на коробку. Если есть место для вашей посылки, вставьте ее и продолжайте заниматься своими делами. Если нет, вы можете подождать, пока освободится место, а затем положить пакет и продолжить заниматься своим делом.

Таким образом, существует общая схема отправки:

  1. Получить в очередиесли вам нужно.
  2. Когда вы достигнете головы очереди, положите свой пакет, если есть место, в противном случае дождитесь места - или, если небуферизованный канал, чтобы кто-то пришел к другому (получите) и положите руку, чтобы получить.

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

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

. В вашем коде вы запускаете вторую программу, которая начинается с multiplyByTwo. Эта одна программа ожидает - один раз - номера, который будет отображаться в канале, или, в этом случае, кого-то, кто ожидает, чтобы отправить номер, так как канал не буферизован. Затем он удваивает (единственное) полученное число, печатает результат и выходит / умирает / оказывается похороненным, чтобы никогда не существовать снова.

Между тем, ваш main ждет, пока кто-то получит - это будет вашвторая программа - пока она не будет готова принять число 3, которое находится в n. Эта часть успешна. Затем ваш main ожидает другого приема, чтобы он мог отправить константу 3.

Пока ваш main ожидает, ваша другая программа выполняет свою работу - или, возможно, закончила свою работу - ивыходы. Теперь во всей системе есть только один «человек» (или суслик, или кто-то еще), ожидающий, пока второй человек - который не существует и никогда не будет рожден - придет, чтобы взять число. Базовая система Go может сказать, что это событие никогда не произойдет, и вот когда вы получите сообщение:

fatal error: all goroutines are asleep - deadlock!

(это также завершает программу).

БуракОтвет Сердара показывает, как у вас может быть ваша вторая программа продолжайте читать номера с канала. Это порождает новую проблему: как вы говорите второму горутину, что больше нет номеров? Ответ в том, что вы можете закрыть канал с помощью close.

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

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

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

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