Synchronization
Catch up 2/10, 12:31pm
In the absence of synchronization, two threads can result in an interweaving order of execution.
When concurrent threads access a shared resource without synchronization, this is known as a race condition
- observed outcome depends on which thread gets to store the value first / last. \
- synchronization will be required for any shared data structures you need to write to(e.g., buffers, queues, lists, hash tables, etc). Read-only data structures don't require synchronization.
When are resources shared?
- local variables are not shared (known as "private" or "data on the stack")
- each thread will have itts own stack
- Never pass/share/store a pointer to a local variable on the stack between threads.
- global variables / static objects are shared: stored in the static data segment, accessible by any thread
- dynamic objects and other heap objects are shared
- allocated from heap with malloc/free or new/delete
Mutual exclusion can be used to synchronize access to shared resource. This allows us to create larger atomic blocks.
- any code that uses mutual exclusion is called a critical section
- what does mutual exclusion enable?
- only one thread at a time can execute in a critical section
- all other threads are forced to wait on entry
- when a thread leaves a critical section, another can enter
- e.g., sharing your bathroom with housemates.
- what requirements are placed on critical section?
- mutual exclusion (mutex): if some thread is in the critical section, then no other is.
- progress: if some thread T is not in critical section, then T cannot prevent some other thread S from entering the critical section. A thread in the critical section will eventually leave it.
- bounded waiting (no starvation): if some T is waiting in the critical section, then it will eventually enter the critical section
- performance: the overhead of entering / exiting a critical section is small with respect to how much work is being done within t.
- safety property: nothing bad happens. No way for two threads to be in the critical section.
- liveness: something good happens (progress, bounded waiting)
- only compromised if an indefinite wait is possible.
- As a rule of thumb, safety and should come first.
How to build a critical section?
- atomic read/write.
- Mutex with atomic r/w
- Alternation is the technique of using a global variable and then setting it to indicate who's turn it is.
- This satisfies the safety requirement.
- Does not satisfy liveness requirement because there's a chance that one thread stops flipping the turn variable, so the other thread never gets to execute because it will always be waiting for their turn.
- Peterson's algorithm: use the technique of having a global variable that indicates who's turn it is but add another variable per thread that indicates when it done executing critical section (
try1is false when not in the critical section) . Then you only waiting if it's not your turn AND that they are currently executing their critical section.- ensures safety with
turn(same as alternation) because it would require both - ensure's liveness because you won't be stuck waiting, at least one of the threads can continuously execute
- ensures safety with
- These don't work with modern hardware due to relaxed memory consistency models. People realized that synchronization is much easier when you have hardware support.
- Alternation is the technique of using a global variable and then setting it to indicate who's turn it is.
- Mutex with atomic r/w
- locks: primitive, minimal semantics, used to build others. It is an object in memory that provides the operation
acquire()(wait until lock is free and then take it to enter a critical section) andrelease()(release a lock to leave a critical section, waking up anyone waiting for it).- Between acquire and release, the thread holds the lock
- Acquire does not return until any previous holder releases, so what happens when these calls are not paired?
- Locks can spin (a spinlock) or block (a mutex).
- semaphores: basic, easy to get the hang of, but hard to program with
- monitors: high-level, requires language support, operations implicit
Linked from Operating Systems