Search code examples
gogoroutine

How does goroutine schedule work with GOMAXPROCS?


I am so confused by goroutines.

Here is the code

func main() {
    // runtime.GOMAXPROCS(1)
    go spinner(100 * time.Millisecond)
    const n = 45
    fibN := fib(n) // slow
    fmt.Printf("\rFibonacci(%d) = %d\n", n, fibN)
}

func spinner(delay time.Duration) {
    for {
        for _, r := range `-\|/` {
            fmt.Printf("\r%c", r)
            time.Sleep(delay)
        }
    }
}

func fib(x int) int {
    if x < 2 {
        return x
    }
    return fib(x-1) + fib(x-2)
}

That is a simple goroutine tutorial code which using goroutine to show an ASCII animation when calculating Fibonacci.

When I set GOMAXPROCS to 1, I think that there will be only one thread to execute goroutine and the Fibonacci function doesn't have any point to yield to animation goroutine. But this demo still works. It shows animation while doing calculating.

How does Go do this without goroutine switching?


Solution

  • Among others : the compiler inserts potential switch points at each function call, so each recursive call to fib(...) can yield to the "spinner" goroutine.

    If you try to implement fib without any function call for example :

    // note : this is a truly horrific way to compute the Fibonacci sequence,
    //        don't do this at home
    // simulate the "compute Fibonacci recursively" algorithm,
    // but without any function call
    func fib(n int) int {
        var res = 0
    
        var stack []int
        stack = append(stack, n)
    
        for len(stack) > 0 {
            // pop :
            n = stack[len(stack)-1]
            stack = stack[0 : len(stack)-1]
    
            if n < 2 {
                res += n
                continue
            }
    
            // else : push 'n-1' and 'n-2' on the stack
            stack = append(stack, n-1, n-2)
        }
    
        return res
    }
    

    https://play.golang.org/p/pdoAaBwyscr

    you should see your spinner 'stuck'