Concurrency Programming Essentials

Concurrency Programming Essentials

Core Principles

  1. Keep critical sections pure
  2. Control critical section granularity
  3. Reduce execution time in critical sections
  4. Avoid holding mutexes for long periods
  5. Use atomic operations instead of mutexes when possible

uint32 is the shortest numeric type that supports atomic operations. You can save space for counters using this type.

Timeout Control

Use the context package to gracefully shut down multiple goroutines with timeouts.

How to implement timeout checks without starting extra goroutines? Use CAS (Compare-And-Swap) operations to check if the state remains original. If it has timed out, the state will change to something else.

A common mistake: using <-time.After() inside for-select loops causes memory leaks. The garbage collector won’t reclaim timers before they fire!

func ProcessChannelMessages(ctx context.Context, in <-chan string, idleCounter prometheus.Counter) {
    idleDuration := 5 * time.Minute
    idleDelay := time.NewTimer(idleDuration)
    // Remember to stop the timer
    defer idleDelay.Stop()
    for {
        // Reset and activate the same timer after expiration
        idleDelay.Reset(idleDuration)
        select {
        case s, ok := <-in:
            if !ok {
                return
            }
        // Wait for timer to expire
        case <-idleDelay.C:
            idleCounter.Inc()
        case <-ctx.Done():
            return
        }
    }
}