Golang: For-loop Concurrency Quirk

Julien Etienne
2 min readApr 20, 2022

Here’s a quick problem which implicitly shares a variables to a goroutines by reference. We’re using a WaitGroup to prevent the main() Goroutine exiting before the loop finishes.

Why we are getting 10 when we have conditioned the loop to 9 may be obvious to some but it’s easy to be caught off guard.

A common mistake is to assume that this has something to do with function closures or some advanced scheduling mechanism. Let’s first take a look at for loops as our beef is with the value defying logic.

For loop

A `for` loop is made of 3 main components (there’s more but let’s not complicate things)

  • The for keyword
  • The e.g. i := 0; i < foo; i-- statement
  • The {} body (execution context)

Breakdown

  • The goroutines queue for scheduling on each loop.
  • Meanwhile the for loop finishes before the goroutines schedule and execute.
  • On the last execution of the for loop’s body the for-statement is oblivious to when it has to stop. So when i is equal to 9 it must still follow the incremental rule i++ . Now that i is equal to 10, the conditional rule i < 10 results to false which is what stops the loop from executing the body further.
  • This behaviour is not specific to Go, it applies to all C based programming languages.

We can see similar behaviour in JavaScript. setTimeout is an asynchronous function. In the below code setTimeout is added to the call-stack on each iteration and executes after the loop completes.

But this is not the entire picture.

In Golang, the iterative variable is shared to the body as a pointer.

In JavaScript notice I’m using the old var syntax for declaring a variable. This is because var in JavaScript behaves similarly-ish to var declarations in Golang. The iterative variable is treated like a pointer. On the other hand thelet keyword copies the value from body to statement on each iteration which gives us 0,1,2,3,4,5… (setTimeout is async but not concurrent)

We can solve this in Golang by simply copying by value.

The goal is not to print in order, we’re trying to do things concurrently so we expect a sporadic occurrence of numbers from 0 to 9.

--

--