~/problems / Locks / Lock (mutex)

Basics: thread-safe counter

easy basics ~10 min

A counter keeps its value in a slow external store (think: a row in a database or a key in a cache). The store has two methods:

  • store.read() returns the current value.
  • store.write(value) replaces it.

Implement Counter(store) with one method:

  • increment() -> int: add 1 to the stored value and return the new value.

Many threads call increment() on the same Counter at once. Every call must count: after n calls the store holds start + n, and no two calls may return the same number.

store = ...          # holds 0
c = Counter(store)
c.increment()        # 1
c.increment()        # 2
# 8 threads each calling increment() 50 times -> store holds 402,
# and the 400 returned values are exactly 3..402

The obvious version, v = store.read() + 1; store.write(v); return v, is a read-modify-write: two threads can both read 5 and both write 6, losing an update. The tests use a store that is slow enough to make that happen almost every time.

Constraints:

  • If store.write raises an exception, increment() should let it propagate, and later calls must still work (the lock must not stay held).
  • Don't sleep or busy-wait.
Show hint

Guard the whole read-then-write with one threading.Lock created in __init__, using with self.lock: so it is released even when an exception is thrown.

Topic: Lock (mutex). One thread in a critical section at a time.

0:00
Ctrl ' run · Ctrl ↵ submit
esc