awesome-everything RU
↑ Back to the climb

Base CS from zero

Time and concurrency: reproduce and fix a race

Crux Hands-on project — build a shared counter, run it across many threads to reproduce a lost-update race, then fix it with a lock and prove correctness with before/after counts.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at middle altitude — in the sky
◷ 210 min

Reading about race conditions is not the same as watching one eat your data. Build the smallest program that exhibits a race — many threads incrementing one shared counter — make the wrong number appear with your own eyes, then fix it with a lock and prove the fix with counts, not faith.

Goal

Turn the unit’s ideas into something you can run: structure work into threads (concurrency), let them touch shared state in parallel, reproduce a lost-update race, explain it as a read-add-write interleaving, then add a lock and verify the count is correct and stable across many runs.

Project
0 of 7
Objective

Write a tiny multi-threaded program that increments one shared counter from many threads, reproduce a lost-update race condition, then fix it with a lock — proving the bug and the fix with measured before/after counts, not estimates.

Requirements
Acceptance criteria
  • A before/after table: 10 unsynchronized runs (showing varying, too-low counts) next to 10 locked runs (all equal to the exact expected total).
  • The expected total is stated explicitly (threads x iterations per thread) so the gap in the unsynchronized runs is unambiguous.
  • A one-paragraph explanation of WHY the unsynchronized version loses updates, in terms of read-add-write interleaving — not just 'threads are unsafe'.
  • A one-sentence statement of what the lock changed: it makes the increment indivisible, so only one thread is inside the protected section at a time.
Senior stretch
  • Measure wall-clock time for both versions and report it next to the counts. Explain why the locked version can be slower: the protected section now runs serially, so threads queue for the lock instead of running in parallel.
  • Shrink the contention: have each thread keep a private local count and add it to the shared counter once at the end under the lock. Show the result is still correct but faster, and explain why (far less time spent holding the lock).
  • Replace the lock with an atomic-increment primitive if your language offers one (e.g. atomic types, Go's sync/atomic, Java's AtomicInteger). Show it is correct and explain how it makes read-add-write a single indivisible step without an explicit lock.
  • Add a second shared variable and a deliberately wrong fix (two separate locks, or releasing too early) to see the race reappear, demonstrating that a lock only protects what it actually guards.
Recap

This is the loop you will run whenever concurrency meets shared state: structure the work into threads, let them touch the same data, and watch a race condition produce a wrong, run-to-run-varying answer because counter = counter + 1 is really read-add-write and the threads interleave. Then add a lock to make the shared section indivisible, and prove with counts that the result is now exactly right every time. Doing it once on a toy counter makes the production version — where the shared state is a balance, an inventory, or a session — something you can reason about instead of fear.

shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.