Go

When are the arguments of a go statement evaluated? What does this print?

Question 179HardGo 1.22 to 1.25
package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	x := 1
	wg.Add(2)
	go func(v int) {
		defer wg.Done()
		fmt.Println("arg:", v)
	}(x)
	go func() {
		defer wg.Done()
		fmt.Println("closure:", x)
	}()
	x = 2
	wg.Wait()
}

arg: 1 is guaranteed. The function value and its arguments in a go statement are evaluated in the calling goroutine, at the go statement, just like defer. Only the call itself runs in the new goroutine.

The closure: line can print 1 or 2, and it is a data race. The closure captures the variable x itself, and main writes it with no synchronization while the goroutine reads it. go run -race reports it. The two lines can also appear in either order.

Gotcha: go s.method(expensive()) runs expensive() synchronously in the caller, and the receiver s is evaluated (copied, for a value receiver) at that point too. Wrap the call in a closure if you want the work to happen in the new goroutine.

More on Goroutines & the Scheduler

All 35 Goroutines & the Scheduler questions