Go语言中的递归函数

I started to learn go language days ago. When I tried to start writing some fun codes, I am stuck by a strange behavior.

package main

import "fmt"

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

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

func main() {
    recv(10)
}

when I run the above code, only 10 is printed. When I remove the go before the call to recv, 10 to 0 are printed out. I believe I am misusing go routine here, but I can not understand why it failed start a go routine this way.

When the main function returns, Go will not wait for any still existing goroutines to finish but instead just exit.

recv will return to main after the first "iteration" and because main has nothing more to do, the program will terminate.

One solution to this problem is to have a channel that signals that all work is done, like the following:

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
}

Here, recv will send a single boolean before returning, and main will wait for that message on the channel.

For the logic of the program, it does not matter what type or specific value you use. bool and true are just a straightforward example. If you want to be more efficient, using a chan struct{} instead of a chan bool will save you an additional byte, since empty structs do not use any memory.

A sync.Waitgroup is another solution and specifically intended for the purpose of waiting for an arbitrary amount of goroutines to run their course.

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()
}

I did so and also worked. How come?

package main

import "fmt"

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

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

func main() {
  recv(10)
}