Goroutines

August 5, 2025

Today I’m starting a new series of posts on the topic of goroutines. In the past, I’ve mostly walked through existing documentation (the Go spec, docs for standard library packages, etc), and provided my commentary and explanation.

Starting today, instead, I’ll be going on a topical journey through goroutines. I will, of course, touch on the spec, and some standard library packages, but I’ll be taking my own path through the topic. Let me know what you think about the new approach.


What is a goroutine?

There’s a fair amount of confusion about what a goroutine is, especially among newcomers to the langauge. Before I tackle that, though, let’s look at some example code:

for i := range 10 {
	go func() {
		fmt.Println(i)
	}()
}

What does this code do?

It loops over the numbers 0 through 9, and for each number, it starts a new goroutine that prints the value of i. If you run the code, you’ll likely find one of two outcomes:

  1. The numbers are not printed in order
  2. Some, or all, of the numbers are not printed at all

Both of these outcomes are because goroutines run concurrently—this means that the order of execution is not defined or guaranteed. Neither is it guaranteed that all goroutines will run, or complete, at all, before the program exits.

We’ll address both of those issues in future posts, but for now, let’s focus on the question of what a goroutine is.

A goroutine is not a function

Perhaps the most common misconception about goroutines is that they are functions.

go myFunc()

Given code as above, some may refer to myFunc as a goroutine. This is incorrect. As we can see in the first example, we execute the same (anonymous) function multiple times, each time in a new, separate goroutine. From this, it’s apparent that at minimum, there’s not a one-to-one relationship between functions and goroutines.

But let’s take it one step further, with this silly example:

func foo() {
	for i := range 10 {
		if i %2 == 0 {
			go foo()
		} else {
			foo()
		}
	}
}

Now this function foo() doesn’t do anything useful (please don’t run it!). But it does show that there’s not even a one-to-many relationship between functions and goroutines. In this case, the function foo() is called in a separate goroutine for even numbers, and in the same goroutine for odd numbers. And it’s recursive (it calls itself).

A goroutine is a lightweight thread of execution

A goroutine is a lightweight thread of execution. While functions do execute within goroutines, there is no direct relationship between the two. A single goroutine will typically execute many functions before it exits. And a single function may be executed in many goroutines.

A rough (and technically over-simplified) analogy is that a function is to a goroutine as a car is to a lane on a highway. A single lane can handle multiple cars, and cars often drive in different lanes.


Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Get daily content like this in your inbox!

Subscribe