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

Я изучаю Go и читаю несколько статей о горутинах и каналах. Это кажется действительно хорошей особенностью Go. Но... я не понимаю хорошего/идиоматического способа обеспечить надлежащую очистку того, что я бы назвал "бесполезной горутиной производителя".

Есть статья, который довольно хорошо объясняет проблему, и это вдохновило на этот вопрос. Хотя статья хорошо объясняет проблему, я нахожу решения, которые она предлагает, не очень убедительными, потому что им не хватает общности (например, «просто использовать буферизованные каналы» на самом деле не работает для бесконечного производителя).

Примечание. Вы можете прочитать статью для ознакомления/справки, но я объясню здесь достаточно, чтобы сделать мой вопрос самодостаточным.

Что я имею в виду под «бесполезным продюсером»? Я имею в виду две вещи:

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

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

Поясню на конкретном примере. Ниже приведен пример функции, которая создает «производителя простых чисел» и возвращает канал, позволяющий потребителю получить любое количество простых чисел.

func isPrime(num int) bool { ... details are not relevant... }

func GetPrimes() chan int {
    primes := make(chan int)

    go func() {
        primes <- 2
        primes <- 3
        for candidate := 5; ; candidate += 2 {
            if isPrime(candidate) {
                primes <- candidate
            }
        }
    }()

    return primes
}

Потребитель может использовать это, чтобы получить несколько простых чисел и распечатать их:

func PrintSomePrimes() {
    primes := GetPrimes()
    for i := 0; i < 10; i++ {
        fmt.Println(<-primes)
    }
}

Проблема / вопрос, который у меня есть, заключается в том, что я думаю, что после выхода из этой функции больше нет ссылки на канал primes (кроме той, что от самого производителя). Это означает, что кто угодно больше не может читать простые числа. Производитель простых чисел теперь «бесполезен», поскольку любой потребитель буквально не может прийти и попытаться использовать/читать простые числа, которые он производит.

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

Теперь вопрос... Есть ли хороший "идиоматический способ" остановить эту бесполезную горутину производителя и избежать утечки памяти?

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

Вторая мысль заключалась в том, что, предполагая, что GC каналов не может обеспечить такого рода механику, «второстепенным» решением может быть то, что потребитель сигнализирует о том, что они сделаны, явно закрывая канал: так что-то вроде:

func PrintSomePrimes() {
    primes := GetPrimes()
    defer func() { close(primes) }()
    for i := 0; i < 10; i++ {
        fmt.Println(<-primes)
    }
}

Однако я прочитал тот

One general principle of using Go channels is don't close a channel from the receiver side

Так что кажется, что это не "идиоматично" идти.

Я не понимаю, почему люди продолжают отрицать мои вопросы о go. Я имею в виду отрицательный голос, если вам это «не нравится». Это нормально. Но, может быть, вы могли бы объяснить, почему? Мне этот вопрос показался очень хорошим, на самом деле я отвечаю в розыске (и рад, что так быстро получил ответ от @burak-serdar)

Kris 10.04.2022 20:54

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

super 10.04.2022 21:14
Получение данных из формы с помощью JavaScript - краткое руководство
Получение данных из формы с помощью JavaScript - краткое руководство
Получить данные из формы с помощью JS очень просто: вы запрашиваете элемент формы, передаете его конструктору new FormData() и, наконец, получаете...
Пользовательские правила валидации в Laravel
Пользовательские правила валидации в Laravel
Если вы хотите создать свое собственное правило валидации, Laravel предоставляет возможность сделать это. Создайте правило с помощью следующей...
3 метода стилизации элементов HTML
3 метода стилизации элементов HTML
Когда дело доходит до применения какого-либо стиля к нашему HTML, существует три подхода: встроенный, внутренний и внешний. Предпочтительным обычно...
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
1
2
9
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Идиоматический способ сделать это — уведомить горутину генератора с помощью канала Другой или контекста о том, что его результаты больше не нужны.

func generator(ctx context.Context) chan int {
   ch:=make(chan int)
   go func() {
       for {
          // Generate some value
          select {
             case <-ctx.Done():
                 return
             case ch<-value:
          }
       }
   }()
   return ch
}

func consumer() {
   ctx,cancel:=context.WithCancel(context.Background())
   defer cancel()
   ch:=generator(ctx)
   // Consume values
}

Вышеупомянутая реализация на основе контекста использует отдельный канал (канал готовности) для уведомления генератора. Также можно использовать отдельный канал. Закрытие канала done на принимающей стороне транслирует все генераторы, которые они могут остановить.

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

Kris 10.04.2022 21:05

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

Burak Serdar 10.04.2022 21:13

Другие вопросы по теме