Different Locks
When we need some arbitration on the concurrent read/write requests, the lock is introduced. It reorders these requests as they run in series, which limits the commutativity of these operators.
However, this constraint is not fit for all scenarios, especially when we need concurrecy and consistency.
One Writer and One Reader
When there is only one writer and one reader, a simple exclusive lock is enough. To use the CAS spinlock or mutex, we need benchmarks to measure the critical section time usage, but mostly, mutex is ok.
And if we know and are able to maintain more detailed information of the running state, we can use wait-free data structures to achieve the same target.
One Writer and Multiple Readers
This is a case of classical producer-consumer problem, which also appears in some scenarios of message queue. And the Read-Write-Lock is a usual method to solve this.
It maintains some global (or just non-local) conditions with extra locks to communicate between readers and the writer. In this case, the dirty read and ABA problem is natually solved because there is only one writer.
Multiple Writers and One Reader
It’s really a rare case in actual life, for most writers do write operations based on the previous read data. But if it’s a log-like system, the lock on the array tail to limit write order is enough, just like the memtable in LSM-Tree. Or just shard the race data to isolate writers, and convert this problem into one-write-one-reader case.
Multiple Writers and Multiple Readers
This is often the case that nowadays local and distributed system encounters. If we only need to handle it, a mutex lock is already enough. But as one-writer-multi-readers inspires, we could maintain similar variables to sort the writes and reads, to avoid dirty read/write while also to maintain some degree of concurrency, which is much better than a simple mutex and simpler than complex wait-free data structures. And this is MVCC.