ZetCode

Golang go keyword

last modified May 7, 2025

This tutorial explains how to use the go keyword in Go. We'll cover goroutine basics with practical examples of concurrent execution.

The go keyword starts a new goroutine, which is a lightweight thread managed by the Go runtime. Goroutines enable concurrent execution of functions.

In Go, go is used before function calls to execute them concurrently. Goroutines are more efficient than OS threads and enable highly concurrent programs.

Basic goroutine example

The simplest use of go creates a goroutine from a function call. This example demonstrates concurrent execution.
Note: Using time.Sleep to wait for goroutines is only suitable for simple demonstrations. In production code, use sync.WaitGroup or channels for proper synchronization.

basic_goroutine.go
package main

import (
    "fmt"
    "time"
)

func sayHello() {
    fmt.Println("Hello from goroutine")
}

func main() {
    go sayHello()
    time.Sleep(100 * time.Millisecond)
    fmt.Println("Hello from main")
}

The sayHello function runs concurrently with the main function. We use time.Sleep to wait for the goroutine to complete.

Multiple goroutines

We can create multiple goroutines to execute functions concurrently. This example shows three goroutines running simultaneously.

multiple_goroutines.go
package main

import (
    "fmt"
    "time"
)

func worker(id int) {
    fmt.Printf("Worker %d starting\n", id)
    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    for i := 1; i <= 3; i++ {
        go worker(i)
    }
    
    time.Sleep(2 * time.Second)
    fmt.Println("All workers completed")
}

Each worker goroutine runs independently. The sleep in main ensures all goroutines complete before the program exits.

Anonymous function goroutines

We can create goroutines from anonymous functions. This is useful for quick concurrent operations.

anonymous_goroutine.go
package main

import (
    "fmt"
    "time"
)

func main() {
    go func() {
        fmt.Println("Running in goroutine")
    }()
    
    time.Sleep(100 * time.Millisecond)
    fmt.Println("Running in main")
}

The anonymous function executes concurrently with the main function. This pattern is common for short-lived goroutines.

Goroutines with parameters

We can pass parameters to goroutines just like regular function calls. This example demonstrates parameter passing.

goroutine_params.go
package main

import (
    "fmt"
    "time"
)

func printMessage(msg string) {
    fmt.Println(msg)
}

func main() {
    go printMessage("First message")
    go printMessage("Second message")
    
    time.Sleep(100 * time.Millisecond)
    fmt.Println("Main message")
}

Both goroutines receive their parameters normally. The order of output may vary between runs due to scheduling.

Goroutines and Shared Memory

Goroutines can access shared variables, but this requires synchronization to prevent race conditions. Without synchronization, multiple goroutines modifying a shared variable simultaneously can lead to unpredictable behavior. One way to ensure thread safety is by using a sync.Mutex.

A sync.Mutex (mutual exclusion) ensures that only one goroutine can modify the shared variable at a time. The mutex must be locked before accessing the shared resource and unlocked afterward. This prevents concurrent goroutines from interfering with each other and ensures consistent results.

shared_memory.go
package main

import (
    "fmt"
    "sync"
)

var counter int
var mutex sync.Mutex // Declare a mutex

func increment() {
    mutex.Lock() // Acquire the lock before modifying the shared variable
    counter++
    fmt.Println("Incremented to", counter)
    mutex.Unlock() // Release the lock after modification
}

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            increment()
        }()
    }

    wg.Wait() // Wait for all goroutines to complete
    fmt.Println("Final counter:", counter)
}

Using sync.Mutex prevents race conditions by ensuring only one goroutine can modify counter at a time. The key features include:

Without proper synchronization, multiple goroutines may attempt to modify counter at the same time, leading to unexpected results such as incorrect final values or inconsistent increments. This is known as a race condition, where the outcome depends on the unpredictable timing of concurrent executions.

Using a sync.Mutex ensures safe updates to shared resources, making the code reliable and consistent across multiple executions.

Waiting for goroutines with WaitGroup

The sync.WaitGroup provides a better way to wait for goroutines than sleeping. This example demonstrates proper synchronization.

waitgroup.go
package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Printf("Worker %d starting\n", id)
    // Simulate work
    time.Sleep(200 * time.Millisecond)
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup
    
    for i := 1; i <= 3; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }
    
    wg.Wait()
    fmt.Println("All workers completed")
}

WaitGroup tracks goroutine completion. Add increments the counter, Done decrements it, and Wait blocks until zero.

Goroutines with channels

Channels provide safe communication between goroutines. This example shows goroutines sending and receiving data.

goroutine_channels.go
package main

import "fmt"

func produceMessages(ch chan<- string) {
    for i := 0; i < 5; i++ {
        ch <- fmt.Sprintf("Message %d", i)
    }
    close(ch)
}

func main() {
    ch := make(chan string)
    
    go produceMessages(ch)
    
    for msg := range ch {
        fmt.Println("Received:", msg)
    }
    
    fmt.Println("All messages received")
}

The producer goroutine sends messages through the channel. The main goroutine receives them until the channel is closed.

Source

Go language specification

This tutorial covered the go keyword in Go with practical examples of goroutine creation and management.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Golang tutorials.