liveBook · Manning

Go keyword

As explained in Learn Concurrent Programming with Go

The Go keyword go starts a function invocation as a goroutine without making the caller wait for its completion.

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

Listing 2.3 Main thread calling the doWork() function in parallel
func main() {
    for i := 0; i < 5; i++ {
        go doWork(i)              #1
    }
    time.Sleep(2 * time.Second)   #2
}
#1 Starts a new goroutine that calls the doWork() function
#2 Waits for all of the work to finish using a longer sleep

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

How Learn Concurrent Programming with Go covers it

How deep it goes
Defines asynchronous invocation through runnable work and shared-memory examples, while deferring proper completion coordination and race handling.
Examples
Uses five timed work calls, a shared countdown pointer, and 31 concurrent web-page letter counters.
What you can do after
Apply it via Go's go keyword to launch independent function calls concurrently while accounting for program termination and unsafe shared updates.
How it connects
Connects goroutines with synchronous calls, shared memory, race conditions, condition variables, semaphores, and waitgroups.

Also explained in

Compare how 3 books explain go keyword →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Learn Concurrent Programming with Go. The section links point to the parts of the book it draws on.