Concurrency and Sharing Patterns: Rust, C, JavaScript, Python
This is AI-generated content, published for my own reference.
Concurrency is not just about running multiple things at once. The hard part is deciding how data is shared safely.
Rust, C, JavaScript, and Python approach this problem very differently. Rust uses ownership and type-system guarantees. C relies almost entirely on programmer discipline. JavaScript avoids many issues through a single-threaded event loop. Python uses the GIL, which simplifies threading but limits true parallelism.
This guide compares their common concurrency and sharing patterns side by side.
Rust
Ownership system enforces thread safety at compile time.
| Pattern | Type | Use case |
|---|---|---|
Arc<T> |
Shared ownership (read-only) | Immutable data across tasks |
Arc<Mutex<T>> |
Shared + exclusive access | Write access, low contention |
Arc<RwLock<T>> |
Shared + read-heavy access | Many readers, few writers |
mpsc::channel |
Message passing | Producer-consumer |
broadcast::channel |
Multi-consumer messages | Pub/sub |
tokio::spawn |
Async task | Non-blocking I/O |
std::thread::spawn |
OS thread | CPU-bound work |
Send / Sync traits |
Compiler markers | Compiler rejects unsafe sharing |
Key insight: Rust won't compile if you share data unsafely. No data races at runtime because they're caught at compile time.
C
No built-in safety. Everything is manual and trusts the programmer.
| Pattern | Type | Use case |
|---|---|---|
pthread_mutex_t |
Mutex | Exclusive access to shared memory |
pthread_rwlock_t |
Read-write lock | Many readers, few writers |
sem_t |
Semaphore | Limit concurrent access count |
pthread_cond_t |
Condition variable | Wait for a signal from another thread |
pipe() / socketpair() |
Message passing | Inter-thread or inter-process communication |
Shared memory (mmap, shmget) |
Raw shared memory | Inter-process sharing |
_Atomic / stdatomic.h |
Atomic operations | Lock-free counters, flags |
Key insight: Nothing prevents you from reading/writing shared memory without a lock. Data races are silent bugs — no compiler help, no runtime error, just corruption.
JavaScript (Node.js / Browser)
Single-threaded event loop makes most sharing trivial — but also means no true parallelism in the main thread.
| Pattern | Type | Use case |
|---|---|---|
| Closures / shared references | Same-thread sharing | Default — everything is shared, no locks needed |
Promise / async-await |
Cooperative concurrency | I/O-bound tasks, interleaved on one thread |
setTimeout / queueMicrotask |
Deferred execution | Scheduling work on the event loop |
Worker (Web) / worker_threads (Node) |
True OS threads | CPU-bound parallelism |
SharedArrayBuffer + Atomics |
Shared memory across workers | Low-level lock-free sharing between threads |
postMessage |
Message passing | Worker communication (data is copied/transferred) |
Key insight: The event loop is single-threaded, so normal code never races. But it also can't use multiple cores. Worker threads add parallelism but sharing is explicit and limited (SharedArrayBuffer or message copies).
Python
GIL (Global Interpreter Lock) makes threading deceptively simple but limits parallelism.
| Pattern | Type | Use case |
|---|---|---|
threading.Lock / RLock |
Mutex | Protect shared state between threads |
threading.Event / Condition |
Signaling | Wait/notify between threads |
queue.Queue |
Thread-safe message passing | Producer-consumer |
asyncio / async-await |
Cooperative concurrency | I/O-bound, single-threaded (like JS) |
multiprocessing.Process |
Separate processes | True CPU parallelism (bypasses GIL) |
multiprocessing.Queue / Pipe |
IPC message passing | Communication between processes |
multiprocessing.Value / Array |
Shared memory (IPC) | Shared state across processes |
Key insight: The GIL means only one thread runs Python bytecode at a time. threading gives concurrency (interleaving) but not parallelism. For true parallelism you need multiprocessing (separate processes, separate memory) or C extensions that release the GIL.
Summary
| Safety | Parallelism | Sharing model | |
|---|---|---|---|
| Rust | Compile-time enforced | Full (threads + async) | Ownership + Arc + locks |
| C | None (programmer's job) | Full (pthreads) | Raw pointers + manual locks |
| JavaScript | Implicit (single-threaded) | Limited (Workers) | Event loop + message passing |
| Python | GIL hides races (mostly) | Limited by GIL (use multiprocessing) | GIL + locks or separate processes |
The spectrum runs from C (maximum freedom, zero guardrails) to Rust (maximum freedom, compile-time guardrails) to JS/Python (limited parallelism, so sharing is rarely an issue).