One of the possible issues of implementing concurrent programs in Golang is data race conditions.
This article discusses this topic in detail and techniques used for identifying where race conditions can occur, strategies to prevent them and how Golang handles data shared between different concurrent contexts.
Before we start, if you are new to concurrency in Golang, I’d highly recommend reading these articles first:
- Concurrency in Golang, Goroutines, and Channels Explained
- File Processing Using Concurrency With GoLang
- Mastering Concurrency In Go — With Select, Goroutines, and Channels
What Is a Golang Race Condition?
Race conditions are the outcomes of two different concurrent contexts reading and writing to the same shared data simultaneously, resulting in an unexpected output.
In Golang two concurrent goroutines that access the same variable concurrently will produce a data race in the program.
Atomic operations with Golang
Atomicity is a key concept regarding race conditions, shared data, and critical sections in a concurrent program.
We have talked about a race condition, how different goroutines shared data, what is a critical section, and that we need a strategy to safely access and modify data inside a critical section.
The strategy that a concurrent program needs to follow when accessing and modifying shared variables is to make sure that each operation that happens in a critical section is atomic.
An atomic operation ensures that no other process/context can read/write the shared data until the current process finishes the read/write operation. This means that a shared variable is atomic within the context of accessed/written. This characteristic is critical section of a program shows us that the shared data is concurrency-safe. Thus race conditions will not happen.
Golang provides primitives for memory access synchronization so that operations can be atomic.
It is common when running multiple goroutines, that data needs to be shared among them. If there is no safe strategy to access and modify data, this will result in a data race condition.
When writing concurrent programs, it is essential to identify all the variables shared between goroutines.
A critical section is a block of code where a goroutine attempts to write a shared variable.
Every critical section in a concurrent program must implement a strategy to safely access and modify the shared data.