Locks
Locks
A lock (also called a mutex) guards a critical section. A thread takes the lock, does its work on the shared data, then releases it. While one thread holds the lock, any other thread that asks for it must wait. So only one thread at a time is inside the critical section.
How locks are built
A lock is just a flag: 0 means free, 1 means taken. Checking the flag and then setting it is two steps, so two threads can both see 0 and both walk in. The CPU fixes this with special instructions that check and change a value in one atomic step. Test-and-set writes 1 and hands back the old value; if the old value was 0, you got the lock. Compare-and-swap changes a value only if it still equals what you expect.
Spinning or sleeping
A spinlock keeps checking the flag in a loop. It wastes CPU time while it waits. A sleeping lock asks the OS to put the thread to sleep, and the OS wakes it when the lock is free. Sleeping saves the CPU but costs a context switch. Spinning only pays off when the wait is very short and the holder is running on another core.
Contention
When many threads want the same lock at once, there is contention. They queue up and run one by one, so adding threads does not make the program faster. The fix is smaller locks: one lock per piece of data, like one per bank account, instead of one big lock for everything. Threads that touch different data then don't wait for each other.
Using locks well
Hold a lock for as short a time as you can. Do slow work, like reading a file or calling the network, before you take the lock or after you release it. Always release the lock, even when something fails. A thread that returns early on an error and forgets to unlock leaves every other thread waiting forever.
Remember
- Take the lock, do the work, release it.
- Locks need an atomic instruction like test-and-set.
- Spin for very short waits, sleep for longer ones.
- One busy lock makes threads queue; split it up.
- Hold locks briefly and always release them.