Skip to main content

Command Palette

Search for a command to run...

Concurrency vs Parallelism — A Deep Technical Guide

Gopher Stories — by Harsh Jha

Updated
6 min read

Concurrency and parallelism are fundamental concepts in modern system design. Although often used interchangeably, they solve different problems and operate at different layers. Understanding them is critical for building scalable, high-performance systems — especially distributed systems.


1. Canonical Definitions

Concurrency is about managing multiple tasks at the same time — even if only one is executing.

Parallelism is about executing multiple tasks simultaneously on multiple processing units.


2. Relationship Between Concurrency and Parallelism

PropertyConcurrencyParallelism
LevelSoftwareHardware
Requires multiple coresNoYes
FocusCoordinationSpeed

A system can be concurrent without being parallel, parallel without being concurrent, or both.


3. Execution Models

Concurrent on Single Core

Task A → Task B → Task C → Task A → Task B

Parallel on Multi Core

CPU Core 1: Task A
CPU Core 2: Task B
CPU Core 3: Task C

4. Hardware Considerations

Parallelism interacts with:

  • Cache hierarchies

  • Cache coherence (MESI)

  • NUMA

  • Memory bandwidth

These often limit scalability more than CPU usage.


5. Memory Model & Race Conditions

A race occurs when multiple tasks access shared memory concurrently and at least one modifies it without synchronization.


6. Synchronization Techniques

TechniquePurpose
MutexMutual exclusion
Atomic opsLock-free updates
ChannelsMessage passing
BarriersPhase coordination

7. When to Use Each

Concurrency is ideal for I/O-bound and event-driven systems.
Parallelism is ideal for CPU-bound workloads.


8. Programming Models

Concurrency models:

  • Actor model

  • CSP

  • Async/await

Parallel models:

  • Thread pools

  • Fork-join

  • Data parallelism


9. Why Concurrency Is Hard

Concurrency introduces:

  • Deadlocks

  • Live locks

  • Races

  • Starvation

  • Ordering bugs

These bugs are non-deterministic and hard to reproduce.


10. Go’s Concurrency Model

Go provides:

  • Goroutines (lightweight execution units)

  • Channels (safe communication)

  • A user-space scheduler


11. How Go Wins at Concurrency and Parallelism

Go was designed from day one to make concurrency cheap, safe, and natural — while also allowing efficient parallel execution on modern multi-core hardware.

Its model is inspired by Communicating Sequential Processes (CSP), where:

Don't communicate by sharing memory. Share memory by communicating.


11.1 Go’s Concurrency Model

Go introduces:

  • Goroutines — extremely lightweight threads (~2 KB stack)

  • Channels — typed message queues for safe communication

  • Scheduler (G-M-P model) — user-space scheduling of goroutines onto OS threads

Diagram: Go Scheduling Model

+-------------+        +-------------+
| Goroutine G1| -----> |             |
+-------------+        |             |
+-------------+        |   Go        |       +-----------+
| Goroutine G2| -----> | Scheduler   | ----> | OS Thread |
+-------------+        |             |       +-----------+
+-------------+        |             | ----> | OS Thread |
| Goroutine G3| -----> |             |       +-----------+
+-------------+        +-------------+

Goroutines are multiplexed onto a small pool of OS threads.

This allows:

  • Millions of concurrent tasks

  • Very low context-switch overhead

  • Efficient I/O waiting


11.2 Parallelism in Go

Parallelism is controlled by:

runtime.GOMAXPROCS(n)

This sets how many OS threads can execute Go code simultaneously.

Diagram:

CPU Core 1   CPU Core 2   CPU Core 3
   |            |            |
 OS Thread    OS Thread    OS Thread
   |            |            |
 Goroutine    Goroutine    Goroutine

So Go supports:

  • Concurrency through goroutines

  • Parallelism through OS threads and cores


11.3 Why Goroutines Are Superior to Threads

FeatureOS ThreadsGoroutines
Stack size~1–8 MB~2 KB
Creation costHighVery low
SchedulingKernelUser-space
ScalabilityThousandsMillions

This is why Go can handle:

  • Massive concurrent connections (e.g., microservices, APIs)

  • High throughput pipelines

  • Event-driven distributed systems


12. Go for Distributed Systems

Distributed systems are inherently:

  • Concurrent (many requests, nodes, events)

  • Partially parallel (replication, computation)

  • I/O heavy (network, disk)

  • Failure-prone

Go matches these needs extremely well.


12.1 Networking is First-Class

Go’s net/http, grpc, and net libraries are:

  • High performance

  • Concurrent by default

  • Simple to use

Example:

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    go processRequest(r)
})

Each request can be handled concurrently with minimal overhead.


12.2 Channels Enable Safe Coordination

Instead of locks:

results := make(chan int)

go worker(results)
go worker(results)

r1 := <-results
r2 := <-results

No shared memory, no races, no mutex complexity.


12.3 Go Avoids Common Concurrency Pitfalls

  • Data races reduced via channels

  • Deadlocks easier to reason about

  • Memory visibility handled by runtime


13. Distributed System Example in Go

Scenario: Fan-out / Fan-in pattern

          Request
             |
         +---+---+
         |       |
      Worker1  Worker2
         |       |
         +---+---+
             |
          Response

Go code:

func fanOutFanIn(tasks []Task) Result {
    results := make(chan Result)

    for _, t := range tasks {
        go func(task Task) {
            results <- process(task)
        }(t)
    }

    final := aggregate(results, len(tasks))
    return final
}

Simple, scalable, expressive.


14. Why Prefer Go Over Other Languages?

LanguageConcurrencyParallelismSimplicityDistributed Use
JavaHeavy threadsYesMediumYes
PythonAsync only (GIL limits)No true CPU parallelismEasyMedium
Node.jsEvent-loopSingle-threadedEasyMedium
C++Manual threadsYesHardHard
GoBuilt-inBuilt-inSimpleExcellent

Go gives you:

  • Concurrency by design

  • Parallelism by configuration

  • Simple syntax

  • Strong performance

  • Safe abstractions


15. Final Takeaway

Go doesn't just support concurrency and parallelism — it unifies them into a single mental model.

  • Goroutines make concurrency easy.

  • Scheduler enables parallel execution.

  • Channels make coordination safe.

  • Runtime handles complexity.

This is why Go is a natural fit for modern distributed systems: scalable, concurrent, networked, and resilient by design.


References

This article is based on a combination of official documentation, foundational literature, and practical engineering resources listed below.

  1. Katherine Cox-Buday — Concurrency in Go
    Practical guide to goroutines, channels, and concurrency patterns in Go.

  2. Martin Kleppmann — Designing Data-Intensive Applications
    Authoritative book on scalable, reliable, and distributed system design.

  3. C.A.R. Hoare — Communicating Sequential Processes
    Foundational theory behind Go’s channel-based concurrency model.

  4. Go Official Documentation — Concurrency & Memory Model
    https://go.dev/doc/
    https://go.dev/ref/mem

  5. Rob Pike — Concurrency is not Parallelism (Talk / Article)
    https://blog.golang.org/waza-talk

  6. Docker Documentation — Architecture Overview
    https://docs.docker.com/get-started/overview/