Coordinate Counter Updates Across Threads
TeslaSoftware Engineer · OnsiteLast updated
Tests multi-threaded synchronization, atomic operations, barrier synchronization, and coordinated condition handling across concurrent threads.
Problem Statement
In concurrent telemetry collection systems, data streams often require snapshotting at both milestone thresholds (event-driven) and fixed intervals (time-driven).
You are tasked with coordinating two worker threads that concurrently increment a shared counter starting at 0.
Implement a class or runner function that coordinates two threads, Thread-A and Thread-B, with the following requirements:
- Synchronized Launch: Neither thread may begin incrementing until both threads are initialized and ready to start simultaneously. The launch timestamp must be recorded at this synchronization point.
- Shared State: Both threads increment the same shared counter. Every increment must be thread-safe (no lost updates).
- Event-Driven Snapshot (Thread-A):
Thread-Amonitors the counter value. When the counter reaches or exceedstarget_count(e.g.,10),Thread-Arecordssnapshot_Awith the observed counter value. - Time-Driven Snapshot (Thread-B):
Thread-Btakessnapshot_Bof the counter value exactly whendelay_sec(e.g.,1.0second) has elapsed relative to the synchronized launch time. - Graceful Shutdown: Once both snapshots have been taken, both threads must terminate cleanly, and the function must return the combined results.
Function Signature
from typing import Tuple
def run_coordinated_counter(target_count: int = 10, delay_sec: float = 1.0) -> Tuple[int, int, int]:
"""
Coordinates two threads incrementing a shared counter.
Returns:
(snapshot_a, snapshot_b, final_counter_val)
"""
pass
Constraints
target_count >= 10.05 <= delay_sec <= 5.0- All increments and reads must be synchronized to prevent race conditions.
- Neither thread may spin indefinitely after its required snapshot is completed.
Example
snapshot_a, snapshot_b, final_val = run_coordinated_counter(target_count=10, delay_sec=0.2)
# snapshot_a >= 10 (the value Thread-A observed when threshold reached)
# snapshot_b >= snapshot_a (or vice-versa depending on speed, but >= 0)
# final_val >= max(snapshot_a, snapshot_b)
Explanation:
Both threads start at T0. The shared counter increases rapidly. Thread-A observes counter >= 10 and stores snapshot_a = 10 (or slightly higher if another thread incremented concurrently). Meanwhile, at T0 + 0.2s, Thread-B records the current counter value as snapshot_b. Once both conditions are fulfilled, both threads exit, and the final state is returned.
Edge Cases to Consider
- The counter reaching
target_countbefore or afterdelay_secexpires. - Ensuring one thread does not terminate and leave the shared lock in an inconsistent state.
- Ensuring worker loops yield or pause appropriately so CPU usage does not starve other system threads.
Hints
Hint 1
Use a synchronization barrier (such as `threading.Barrier` in Python) to ensure both threads launch at the exact same instant.
Hint 2
Use a mutual exclusion lock (`threading.Lock`) or an atomic integer primitive to protect the counter increments and reads from data races.
Hint 3
Use a shared event (`threading.Event`) or boolean flags under the lock to signal when both snapshots have been recorded so both worker loops can terminate cleanly.
Follow-up questions
- How would you guarantee that Thread-B takes its snapshot with millisecond-level precision without burning 100% CPU in a busy-wait loop?
- If Python's GIL were removed (or in C++/Java), how would you replace the mutex lock with atomic primitives like `compare_and_swap` or `fetch_add`?
- How would you scale this coordination pattern from 2 threads to N worker threads with multiple heterogeneous snapshot triggers?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.