Как использовать каналы в го? - PullRequest
0 голосов
/ 14 февраля 2019

У меня есть функция, которая принимает массив int и выводит их в канал.

func Dump(a []int, ch chan int) {
    for i := range a {
        ch <- i
    }
    close(ch)
}

Эта основная сборка не выполняется:

func main() {
    ch := make(chan int)
    arr := []int{1, 2, 3, 4, 5}
    Dump(arr, ch)
    for i := range ch {
        fmt.Printf("Got %v\n", i)
    }
}

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

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.Dump(0xc000078f48, 0x5, 0x5, 0xc00006e060)
        /Users/300041738/go-workspace/src/test.go:7 +0x43
main.main()
        /Users/300041738/go-workspace/src/test.go:15 +0x9b
exit status 2

Однако эта сборка:

func main() {
    ch := make(chan int)
    arr := []int{1, 2, 3, 4, 5}
    go Dump(arr, ch)
    for i := range ch {
        fmt.Printf("Got %v\n", i)
    }
}

Почему я должен написать, что идти впереди Dump?Я не хочу выгружать содержимое массива асинхронно.

Ответы [ 3 ]

0 голосов
/ 14 февраля 2019

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

В go есть концепция конвейерной обработки.Ваша Dump функция в основном действует здесь как источник конвейера.Вы можете изменить свою функцию Dump на что-то вроде этого:

func Dump(a []int) chan int {
  ch := make(chan int)
  go func() {
    for i := range a {
      ch <- i
    }
    close(ch)
  }()
  return ch
}

Обратите внимание, что сейчас я пишу в канал в отдельной процедуре перехода.

0 голосов
/ 14 февраля 2019

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

Для иллюстрации:

  1. Dump() вызвана функция
  2. Выполнение первой итерации for loop
  3. Запись i значения в канал
  4. Запуск второй итерации for loop
  5. Попытка записать следующее значение i в канал, но оно блокируется,никто не читал первое значение.
  6. Поскольку вся работа выполняется в единственной главной программе, все приложение заблокировано.В результате тупик.

Это происходит при добавлении использования программы (go Dump(arr, ch)):

  1. Dump(), вызываемая в отдельной программе
  2. Выполнение первой итерации for цикла
  3. Запись i значения в канал
  4. Выполнение второй итерации for цикла
  5. Попытка записи следующейi значение для канала, но оно блокируется, никто не прочитал первое значение.
  6. Запрограммирован Goroutine (где мы запускаем Dump()), но у нас есть основная программа и планировщик go, переключающий элемент управления наглавная программа
  7. Это означает, что эта строка фактически выполнена for i := range ch.И наконец, оно читает первое значение!
  8. Теперь Dump() подпрограмма разблокирована, и можно записать второе значение.
  9. Элемент управления будет передаваться между двумя подпрограммами, пока вся работа не будет завершена.

Обратите внимание, точный порядок выполнения может отличаться (в зависимости от логики планировщика go).Чтобы поиграть с ним, вы можете добавить печать в функцию Dump() для случая с go Dump(arr, ch):

func Dump(a []int, ch chan int) {
    for i := range a {
        fmt.Printf("Write %v\n", i)
        ch <- i
    }
    close(ch)
}

Вы увидите, что сообщения Write и Got будут смешиваться.

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

Используя буферный канал, скажем, с размером n, вы можете делать n запись без блокировкии читает.Я бы порекомендовал вам ознакомиться с основами каналов Go .

0 голосов
/ 14 февраля 2019

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

Так что длявесело, попробуйте это:

import "fmt"

func Dump(a []int, ch chan int) {
    for i := range a {
        ch <- i
    }
    close(ch)
}

func main() {
    arr := []int{1, 2, 3, 4, 5}
    ch := make(chan int, len(arr)) //specify channel buffer length equal to arr size
    Dump(arr, ch)
    for {
        i, ok := <- ch
        if ok {
            fmt.Println("received a number !", i)
        } else {
            fmt.Println("channel is closed, we're done here")
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...