100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Go

Goroutines in Go

Goroutines are lightweight, runtime-managed threads that let Go programs run functions concurrently with minimal overhead.

ConcurrencyBeginner7 min readJul 8, 2026
Analogies

Introduction

A goroutine is a lightweight thread of execution managed by the Go runtime rather than the operating system. You start one by prefixing a function call with the go keyword, and the Go scheduler multiplexes potentially thousands of goroutines onto a small number of OS threads. Goroutines start with a tiny stack (a few kilobytes) that grows and shrinks dynamically, which is why Go programs can comfortably run tens of thousands of concurrent goroutines where OS threads would exhaust memory.

🏏

Cricket analogy: A goroutine is like a substitute fielder the umpire waves onto the field instantly rather than waiting for a formal squad change; the Go scheduler, like a captain, juggles thousands of such fielders across just a handful of actual playing positions.

Syntax

go
go functionName(arguments)

// with an anonymous function
go func() {
    // code to run concurrently
}()

Explanation

The go statement does not block; it schedules the function to run concurrently and immediately continues to the next line. There is no guarantee about when the goroutine actually starts running relative to the caller. A critical gotcha for beginners: the main function itself runs in a goroutine, and when main returns, the entire program exits immediately, even if other goroutines are still running and haven't finished their work. This is why real programs need a synchronization mechanism, such as sync.WaitGroup or a channel, to wait for goroutines to complete before main exits.

🏏

Cricket analogy: The non-blocking go statement is like a captain sending a fielder to a new position and immediately turning attention to the next over, not waiting to watch the fielder actually arrive there.

Example

go
package main

import (
    "fmt"
    "sync"
)

func sayHello(name string, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Println("Hello,", name)
}

func main() {
    var wg sync.WaitGroup
    names := []string{"Alice", "Bob", "Carol"}

    for _, n := range names {
        wg.Add(1)
        go sayHello(n, &wg)
    }

    wg.Wait()
    fmt.Println("All goroutines finished")
}

Output

The three "Hello, <name>" lines print in a non-deterministic order (the scheduler decides which goroutine runs first), followed by "All goroutines finished" once wg.Wait() unblocks. Example output: Hello, Bob Hello, Alice Hello, Carol All goroutines finished

🏏

Cricket analogy: The non-deterministic print order is like three fielders shouting appeal calls in whatever order they physically react, with the umpire's final decision ("All goroutines finished") only coming after every appeal has been heard.

Key Takeaways

  • A goroutine is started with the go keyword and runs concurrently with the caller.
  • Goroutines are extremely lightweight compared to OS threads, starting with a small growable stack.
  • If main returns, the program exits immediately even if other goroutines haven't finished.
  • Use sync.WaitGroup or channels to coordinate and wait for goroutines to complete.
  • Goroutine execution order is not guaranteed; never assume a specific interleaving.

Practice what you learned

Was this page helpful?

Topics covered

#Go#GoProgrammingStudyNotes#Programming#GoroutinesInGo#Goroutines#Syntax#Explanation#Example#StudyNotes#SkillVeris