The Go keyword go starts a function call in a new goroutine. The caller continues immediately rather than waiting for that call to return, making the invocation asynchronous; an ordinary call is synchronous. section 2.3.1
Prefixing a call with go can turn repeated sequential work into concurrent work. Five one-second work calls that take about five seconds sequentially can begin together and finish in about two seconds, but their start and finish output is not ordered by identifier. section 2.3.1
Compare go doWork(i) with a normal call: the loop launches each goroutine, then main sleeps so the process does not exit before they run. section 2.3.1
Starting a goroutine does not keep a program alive. When main finishes, the process terminates even if goroutines are still running. Sleeping can demonstrate the issue, but it is an unreliable completion strategy because work duration can vary; condition variables, semaphores, and waitgroups are introduced as ways to wait for tasks. section 2.3.1 section 3.2.3
Countdown with shared memory
A countdown starts with go countdown(&count), while main repeatedly reads count. Without go, countdown runs first for five seconds, reaches zero, and main's reading loop never runs. With go, both goroutines access the same variable concurrently. section 3.2.1
Listing 3.1 Goroutines sharing a variable in memory
package main import ( "fmt" "time" ) func main() { count := 5 #1 go countdown(&count) #2 for count > 0 { #3 time.Sleep(500 * time.Millisecond) #3 fmt.Println(count) #3 } } func countdown(seconds *int) { for *seconds > 0 { time.Sleep(1 * time.Second) *seconds -= 1 #4 } } #1 Allocates memory space for an integer variable #2 Starts goroutine and shares memory at the variable reference #3 The main() goroutine reads the value of the shared variable every half second. #4 The goroutine updates the value of the shared variable.
The listing shows a pointer passed to a goroutine: countdown decrements the shared integer while main reads it every half second. section 3.2.1
The keyword can also launch one task per web page: 31 goroutines download documents and count letters concurrently. Sharing the same frequency slice across those goroutines produces erroneous counts because of a race condition, so faster execution alone does not make shared updates safe. section 3.2.3
Listing 3.4 main() function creating goroutines and sharing the frequency slice
func main() { var frequency = make([]int, 26) for i := 1000; i <= 1030; i++ { url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", i) go countLetters(fmt.Sprintf(url), frequency) #1 } time.Sleep(10 * time.Second) #2 for i, c := range allLetters { fmt.Printf("%c-%d ", c, frequency[i]) #3 } } #1 Starts a goroutine that calls the countLetters() function #2 Waits for the goroutines to finish #3 Outputs each letter with its frequency
This example launches countLetters once per URL with go, pauses before printing results, and exposes the unsafe shared frequency slice. section 3.2.3


