A plain Lock lets one thread in at a time. But readers don't change anything, so many of them can safely look at the data together. Only a writer needs to be alone.
Implement RWLock with four methods:
acquire_read(): block while a writer holds the lock, then enter as a reader. Any number of readers may hold it at once.release_read(): leave as a reader.acquire_write(): block until nobody holds the lock (no readers, no writer), then enter as the only writer.release_write(): leave as the writer.
rw = RWLock()
# threads R1, R2: rw.acquire_read() -> both get in together
# thread W: rw.acquire_write() -> blocks until R1 and R2 have both called release_read()
# thread R3: rw.acquire_read() -> blocks while W holds the lock, gets in after release_write()
You don't need fairness here: it's fine if a steady stream of readers keeps a writer waiting. Callers always pair each acquire with the matching release. Don't sleep or busy-wait.
Show hint
Track readers (a count) and writer (a bool) behind one threading.Condition: readers wait_for(not writer), writers wait_for(not writer and readers == 0), and every release that could unblock someone calls notify_all().