liveBook · Manning

Race condition

As explained in Learn Concurrent Programming with Go

A race condition is a concurrency defect in which program behavior depends on the timing or interleaving of unsynchronized operations.

A race condition is a concurrency error in which a program’s result depends on the timing of independent events. It occurs when concurrent executions share a resource without proper synchronization and interfere with one another, producing unexpected behavior, data corruption, or a crash that may be hard to reproduce. section 3.3 section 9.1

Shared memory makes the risk concrete: one goroutine can write a value, then find that another goroutine has changed it before it reads it again. The instructions that must run without such interference form a critical section. section 3.2.3 section 3.5

A bank balance that changes by chance

Two goroutines share a bank account starting at 100. One adds 10 and the other subtracts 10, each one million times, so the expected final balance is 100. Without synchronization, repeated runs can instead leave millions above zero or below zero because both goroutines can overlap while updating the same variable. section 3.3.1

Treat these as warning signs and responses:

  • Look for goroutines that read or write the same variable, slice, or other shared resource concurrently. section 3.2.3 section 3.3.3
  • Do not rely on yielding with runtime.Gosched() to fix a race; it can change how often the error appears but provides no protection. section 3.3.2
  • Choose a coordination approach appropriate to the problem: protect shared critical sections or avoid unnecessary shared memory through communication. section 3.3.3 section 1.5.2
Listing 4.1 Stingy’s and Spendy’s functions using a mutex
package main

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

func stingy(money *int, mutex *sync.Mutex) {    #1
    for i := 0; i < 1000000; i++ {
        mutex.Lock()                            #2
        *money += 10
        mutex.Unlock()                          #3
    }
    fmt.Println("Stingy Done")
}

func spendy(money *int, mutex *sync.Mutex) {    #1
    for i := 0; i < 1000000; i++ {
        mutex.Lock()                            #2
        *money -= 10
        mutex.Unlock()                          #3
    }
    fmt.Println("Spendy Done")
}
#1 Accepts a pointer to the shared mutex struct
#2 Locks the mutex before entering the critical section
#3 Unlocks after exiting the critical section

A mutex brackets each update with Lock and Unlock, allowing only one goroutine to modify the shared balance at a time. section 4.1.1

In Go, sync.Mutex provides Lock() and Unlock() for exclusive access to a critical section. A goroutine that reaches an already locked mutex waits until it is released; protecting both writes and reads of the shared balance produces the expected result. Go’s race detector can also help spot races in code. section 4.1.1 section 3.5

Channels and communicating sequential processes offer another model: isolated goroutines communicate and synchronize by sending messages. This can reduce race-condition risk, although shared-memory primitives such as mutexes and condition variables may be a better-performing choice for some problems. section 1.5.2

How Learn Concurrent Programming with Go covers it

How deep it goes
Defines timing-dependent failures, traces shared-memory interference, and demonstrates mutex protection; defers broader coordination techniques.
Examples
Uses concurrent letter counting, a shared bank balance, scheduler yielding, and an array-update exercise.
What it assumes
Assumes Go or a similar C-style language while introducing concurrency from little prior experience.
What you can do after
Diagnose/debug with it by recognizing unsafe shared access and using Go’s race detector or mutex protection.
How it connects
Connects race conditions to goroutines, shared memory, critical sections, mutexes, channels, CSP, semaphores, and deadlocks.

Also explained in

Compare how 20 books explain race condition →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Learn Concurrent Programming with Go. The section links point to the parts of the book it draws on.