380 likes | 394 Views
Learn about thread synchronization techniques such as mutex, deadlock prevention, and condition variables. Understand race conditions and how to prevent them. Live coding examples provided.
E N D
Lecture 12 Thread Synchronization
Outline • Thread Synchronization • Race Condition • Mutex • Deadlock • Condition Variable • Live Coding
Thread Synchronization • pthreads has two tools to synchronize their actions: • mutexes • Allow threads to synchronize use of shared resource • conditional variables • Allow threads to inform others that a shared resource has changed state
mutexes • Must guard against multiple threads modifying the same variable at the same time • Or trying to read while another thread is writing..
“critical section” • Section of code that accesses a shared resource • Execution should be atomic • Could lead to a race condition
Outline • Thread Synchronization • Race Condition • Mutex • Deadlock • Condition Variable • Live Coding
Example Race Condition • Two threads reading, incrementing, writing same global variable (glob) thread 1: loop N times: loc = glob; loc++; glob = loc; thread 2: loop N times: loc = glob; loc++; glob = loc;
T1: execution halts after loc=glob; executes.. loc = 2000
T2: executes loop 1000X, glob = 3000
T2: halts T1: resumes
When T1 halted, loc = 2000 loc = 2000 glob = 2001
glob should be 3001... right? glob = 3000 glob = 2001
Race Conditions • Such non-deterministic errors are difficult to find • Error depends on ordering of events • What if T1 halted after glob = loc; ? • (instead of loc = glob; )
Outline • Thread Synchronization • Race Condition • Mutex • Deadlock • Condition Variable • Live Coding
mutexes • mutex => mutual exclusion • Has two states: locked and unlocked • At any one time, at most one thread can hold a lockedmutex • Other threads block on lock() • Only owner of lockedmutex can unlock • “acquire” -> lock • “release” -> unlock
mutexes In general, different mutex for each shared resource • 1-1 relationship Order of events: • lockmutex • access shared resource • unlockmutex
mutexes pthread_mutex_t = PTHREAD_MUTEX_INITIALIZER; • #include <pthread.h> • Compile with –pthread option • Initialize a mutexstatically • After initialization, mutex is unlocked
mutexes intpthread_mutex_init( pthread_mutex_t *mutex, constpthread_mutexattr_t *attr ); • #include <pthread.h> • Compile with –pthread option • Returns 0 on success • Returns positive number on errro • Initializes a mutexdynamically (at runtime) • mutex attributes attr often set to NULL (default)
mutexes intpthread_mutex_destroy( pthread_mutex_t *mutex); • #include <pthread.h> • Compile with –pthread option • Returns 0 on success • Returns positive number on errro • Destroys a mutex initialized with pthread_mutex_init()
Lock a mutex intpthread_mutex_lock( pthread_mutex_t *mutex); • #include <pthread.h> • Compile with –pthread option • Returns 0 on success • Returns positive number on error • Attempts to lockthe mutex • Caller blocks until mutex is unlocked
Unlock a mutex intpthread_mutex_unlock( pthread_mutex_t *mutex); • #include <pthread.h> • Compile with –pthread option • Returns 0 on success • Returns positive number on error • Attempts to unlockthe mutex • Can only unlockmutex that owner has locked
Outline • Thread Synchronization • Race Condition • Mutex • Deadlock • Condition Variable • Live Coding
Deadlocks • Sometimes a thread needs to simultaneously access two or more different shared resources • Deadlock occurs when one or more threads lock on the same set of mutexes • Both threads with block indefinitely
Deadlocks Example • Thread 1 locks mutex 1 • Thread 2 locks mutex 2 • Thread 1 locks mutex 2 (blocks waiting for mutex 2 held by T2) • Thread 2 locks mutex 1 (blocks waiting for mutex 1 held by T1) • Deadlock occurs...
Avoiding Deadlocks • Simplest way: define a hierarchy of mutexes • Must always acquire locks in the same order lock mutex 1 lock mutex 2 unlock mutex 2 unlock mutex 1
Outline • Thread Synchronization • Race Condition • Mutex • Deadlock • Condition Variable • Live Coding
Condition Variables (CVs) Allow threads to signal a change of state mutex prevents multiple threads from accessing a shared variable • CV allows one thread to inform other threads about state changes of a shared resource CVs are always used in conjunction with a mutex
Condition Variables • Static initialization: pthread_cond_tcond = PTHREAD_COND_INITIALIZER;
Condition Variables • Dynamic initialization: intpthread_cond_init( pthread_cond_t *cond, pthread_condattr_t *attr ); • If CV initialized dynamically, it must be destroyed: intpthread_cond_destroy( pthread_cond_t *cond );
Condition Variable Operations • CVs have two principal operations: signal and wait • signal – notifies one (or more) waiting threads that a shared variable's state has changed • wait – block until such a notification signal is received
Condition Variable Broadcast intpthread_cond_broadcast( pthread_cond_t *cond); • #include <pthread.h> • Compile with –pthread option • Returns 0 on success • Returns positive number on error • Wakes up all threads waiting on CV
Condition Variable Wait intpthread_cond_wait( pthread_cond_t *cond, pthread_mutex_t *mutex ); • unlocksthe mutex • blocks on the CV (waits for a signal) • relocksthe mutex
Producer / Consumer Example • Multiple threads produce some resource • Main thread consumes the resource
Producer / Consumer Example Producer(s): lock mutex //produce v++ unlock mutex signal CV Consumer: loop forever: lock mutex //wait for something to consume while( v == 0 ) wait (mutex, CV) while( v > 0 ) v-- unlock mutex
Outline • Thread Synchronization • Race Condition • Mutex • Deadlock • Condition Variable • Live Coding
Live Coding • Recreate race condition loc and glob • Fix race condition with mutex • Create a deadlock • Fix the deadlock • Recreate producer / consumer with mutexes / CVs