Рекурсивная функция в языке go

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

package main

import "fmt"

func recv(value int) {
    if value < 0 {
        return
    }

    fmt.Println(value)
    go recv(value-1)
}

func main() {
    recv(10)
}

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

Ответ 1

Когда возвращается основная функция, Go не будет ждать завершения всех существующих goroutines, но вместо этого просто выйдет.

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

Одним из решений этой проблемы является наличие канала, который сигнализирует, что все работы выполняются следующим образом:

package main

import "fmt"

func recv(value int, ch chan bool) {
    if value < 0 {
        ch <- true
        return
    }

    fmt.Println(value)
    go recv(value - 1, ch)
}

func main() {
    ch := make(chan bool)
    recv(10, ch)

    <-ch
}

Здесь recv отправит одно логическое значение перед возвратом, а main будет ждать этого сообщения на канале.

Для логики программы не имеет значения, какой тип или конкретное значение вы используете. bool и true - просто простой пример. Если вы хотите быть более эффективным, использование chan struct{} вместо chan bool сохранит вам дополнительный байт, поскольку пустые структуры не используют память.

Ответ 2

A sync.Waitgroup - это еще одно решение и специально предназначено для того, чтобы ждать, пока произвольное количество горутинцев начнет свой курс.

package main

import (
    "fmt"
    "sync"
)

func recv(value int, wg *sync.WaitGroup) {
    if value < 0 {
        return
    }

    fmt.Println(value)

    wg.Add(1) // Add 1 goroutine to the waitgroup.

    go func() {
        recv(value-1, wg)
        wg.Done() // This goroutine is finished.
    }()
}

func main() {
    var wg sync.WaitGroup
    recv(10, &wg)

    // Block until the waitgroup signals
    // all goroutines to be finished.
    wg.Wait()
}

Ответ 3

Я сделал это и тоже работал. Почему?

package main

import "fmt"

func recv(value int) {
    if value < 0 {
      return
    }

    fmt.Println(value)
    recv(value - 1)
}

func main() {
  recv(10)
}