liveBook · Manning

Race condition

As explained in Go by Example

A race condition is a concurrency defect in which program behavior depends on the timing or ordering of operations accessing shared state.

A race condition occurs when goroutines access shared state concurrently and the result depends on timing or ordering. It can cause crashes, data corruption, stale writes, or a send on a channel that has already been closed. Prevent it by making shared access orderly rather than assuming concurrent operations will happen in a safe sequence. section 8.2.4 section 6.9

First identify the shared resource and the operations that touch it. A map used by HTTP handlers is shared because each request runs in its own goroutine. For direct protection of shared state, use a mutex: Lock grants one goroutine exclusive access, and Unlock lets another proceed. Use an RWMutex when reads can proceed together while writes remain exclusive. section 8.2.4 section 8.2

Listing 8.7 RWMutex protection (link/shortener.go)
package link

import (
    "sync"
    . . .
)

// Shortener shortens and resolves [Link] values from an in-memory storage.
// It's safe for concurrent use from multiple goroutines.
type Shortener struct {
    muLinks sync.RWMutex  #1
    links   map[Key]Link
}

func (s *Shortener) Shorten(_ context.Context, lnk Link) (Key, error) {
    . . .
    s.muLinks.Lock()  #2
    defer s.muLinks.Unlock()  #3

    if _, ok := s.links[lnk.Key]; ok {
        return "", fmt.Errorf(. . .)
    }
    if s.links == nil {
        s.links = map[Key]Link{}
    }
    s.links[lnk.Key] = lnk

    return lnk.Key, nil
}

func (s *Shortener) Resolve(_ context.Context, key Key) (Link, error) {
    . . .
    s.muLinks.RLock()  #4
    defer s.muLinks.RUnlock()  #5

    lnk, ok := s.links[key]
    if !ok {
        return Link{}, fmt.Errorf(. . .)
    }

    return lnk, nil
}

The Shortener keeps an in-memory map safe by locking writes with Lock and Unlock, while Resolve uses RLock and RUnlock for concurrent reads. Deferring unlocks keeps every return path from leaving the resource locked. section 8.2.4

Choose atomic types for simple shared counters. An HTTP test server handles requests in separate goroutines, so incrementing and reading a normal counter would race. atomic.Int64 provides Add for increments and Load for reading the final count without exposing the counter to unsynchronized access. section 7.6.2 section 7.8

Count concurrent HTTP requests safely

Run a client with five goroutines sending 10 requests to a test server. Declare an atomic.Int64 counter, call Add in the server handler, drain the result iterator, then call Load and check that the count is 10. The counter remains safe even though handlers run concurrently. section 7.6.2

Coordinate channel lifetime as carefully as shared memory. In a dispatcher, workers send results while a monitoring goroutine waits for them to finish and only then closes the output channel. Closing early risks a worker sending to a closed channel; closing at completion tells receivers that no more results will arrive. section 6.9

Use these practical checks when concurrent code changes:

  • Run tests in parallel when appropriate, but account for state that tests or subtests may share. section 3.4
  • Run the race detector to find race-condition issues; test coverage alone does not guarantee correctness. section 3.6
  • Prefer mutexes for straightforward shared-state protection rather than adding channels solely to synchronize a map or similar resource. section 8.2.4

How Go by Example covers it

How deep it goes
Explains race hazards through atomic counters, mutex locking, read/write locking, and safe channel closure.
Examples
Uses an HTTP request counter with atomic.Int64, a locked link map, and a dispatcher output channel.
What you can do after
Apply it via sync/atomic and sync mutexes to protect counters and in-memory state under concurrent access.
How it connects
Connects goroutines, shared maps, mutexes, RWMutexes, channels, atomic types, HTTP handlers, and the race detector.

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 Go by Example. The section links point to the parts of the book it draws on.