350 likes | 578 Views
Pthread (continue). General pthread program structure Encapsulate parallel parts (can be almost the whole program) in functions. Use function arguments to parameterize what a particular thread does. Usually needs to know myid and nprocs . Add synchronization when necessary
E N D
Pthread (continue) • General pthread program structure • Encapsulate parallel parts (can be almost the whole program) in functions. • Use function arguments to parameterize what a particular thread does. • Usually needs to know myid and nprocs. • Add synchronization when necessary • Call pthread_create() with the function and arguments, save thread identifier returned. • Call pthread_join() with that thread identifier
Thread synchronizations • When more than one thread works on the same task, the threads often need to coordinate their activities to ensure correct behavior. • Coordination that results in synchronization or communication is the inherent price to pay when going multithreading. • Coordination often means waiting!
Motivating Example: Too Much Milk • Two robots are programmed to maintain the milk inventory at a store… • They are not aware of each other’s presence… Robot: Dumber Robot: Dumb
Dumb 4:00 Look into fridge: Out of milk Dumber Motivating Example: Too Much Milk
Dumb 4:00 Look into fridge: Out of milk 4:05 Head for the warehouse Dumber Motivating Example: Too Much Milk
Dumb 4:05 Head for the warehouse Dumber 4:10 Look into fridge: Out of milk Motivating Example: Too Much Milk
Dumb Dumber 4:10 Look into fridge: Out of milk 4:15 Head for the warehouse Motivating Example: Too Much Milk
Dumb 4:20 Arrive with milk Dumber 4:15 Head for the warehouse Motivating Example: Too Much Milk
Dumb 4:20 Arrive with milk Dumber 4:15 Head for the warehouse Motivating Example: Too Much Milk
Dumb 4:20 Arrive with milk 4:25 Go party Dumber Motivating Example: Too Much Milk
Dumb 4:20 Arrive with milk 4:25 Go party Dumber 4:30 Arrive with milk: “Uh oh…” Motivating Example: Too Much Milk
Common coordination constructs • Critical section: a piece of code that only one thread can execute at a time • Only one thread can go get milk at one time. • Mutual exclusion: ensure one thread can do something without the interference of other threads • When I print, nobody else should be printing.
Common coordination constructs • Synchronization: use atomic operations to ensure cooperation among threads • Event synchronization T1 T2 … … X = 400 Y = X+1 … ... T1 T2 … … X = 400 …. X ready … wait for X Y=X+1 … ...
Pthreads synchronization support • Mutex locks • Critical session and mutual exclusion • Condition variables • Event synchronization • Semaphores • Both (in UNIX, not pthread)
Mutex locks: lock/unlock • pthread_mutex_lock(pthread_mutex_t *mutex); • Tries to acquire the lock specified by mutex • If mutex is already locked, then the calling thread blocks until mutex is unlocked. • At one time, only one thread can get the lock
Mutex locks: lock/unlock • pthread_mutex_unlock(pthread_mutex_t *mutex); • If the calling thread has mutex currently locked, this will unlock the mutex. • If other threads are blocked waiting on this mutex, one will unblock and acquire mutex. • Which one is determined by the scheduler.
Lock and critical section • A lock prevents a thread from doing something • A thread should lock before entering a critical section • A thread should unlock when leaving the critical section • A thread should wait if the critical section is locked • Synchronization often involves waiting
Mutex lock– for mutual exclusion int counter = 0; void *thread_func(void *arg) { int val; /* unprotected code – why? */ val = counter; counter = val + 1; return NULL; }
Mutex example int counter = 0; ptread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void *thread_func(void *arg) { int val; /* protected by mutex */ Pthread_mutex_lock( &mutex ); val = counter; counter = val + 1; Pthread_mutex_unlock( &mutex ); return NULL; }
Condition variables – and event synchronization • Think of Producer – consumer problem • Producers and consumers run in separate threads. • Producer produces data and consumer consumes data. • Producer has to inform the consumer when data is available • Consumer has to inform producer when buffer space is available
Condition variables: wait • Pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) • Blocks the calling thread, waiting on cond. • Unlock the mutex • Re-acquires the mutex when unblocked.
Condition variables: signal • Pthread_cond_signal(pthread_cond_t *cond) • Unblocks one thread waiting on cond. • The scheduler determines which thread to unblock. • If no thread waiting, then signal is a no-op
/* Globals */ int data_avail = 0; pthread_mutex_t data_mutex = PTHREAD_MUTEX_INITIALIZER; void *producer(void *) { Pthread_mutex_lock(&data_mutex); Produce data Insert data into queue; data_avail=1; Pthread_mutex_unlock(&data_mutex); }
void *consumer(void *) { while( !data_avail ); /* do nothing – keep looping!!*/ Pthread_mutex_lock(&data_mutex); Extract data from queue; if (queue is empty) data_avail = 0; Pthread_mutex_unlock(&data_mutex); consume_data(); }
int data_avail = 0; pthread_mutex_t data_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cont_t data_cond = PTHREAD_COND_INITIALIZER; void *producer(void *) { Pthread_mutex_lock(&data_mutex); Produce data Insert data into queue; data_avail = 1; Pthread_cond_signal(&data_cond); Pthread_mutex_unlock(&data_mutex); }
void *consumer(void *) { Pthread_mutex_lock(&data_mutex); while( !data_avail ) { /* sleep on condition variable*/ Pthread_cond_wait(&data_cond, &data_mutex); } /* woken up */ Extract data from queue; if (queue is empty) data_avail = 0; Pthread_mutex_unlock(&data_mutex); consume_data(); }
A note on condition variables • A signal is forgotten if there is no corresponding wait that has already occurred. • If you want the signal to be remembered, use semaphores.
Semaphores • Counters for resources shared between threads. Sem_wait(sem_t *sem) • Blocks until the semaphore vale is non-zero • Decrements the semaphore value on return. Sem_post(sem_t *sem) • Unblocks the semaphore and unblocks one waiting thread • Increments the semaphore value otherwise
Pipelined task parallelism with semaphore P1: for (I=0; I<num_pics, read(in_pic); I++) { int_pic_1[I] = trans1(in_pic); sem_post(event_1_2[I]); } P2: for (I=0; I<num_pics; I++) { sem_wait(event_1_2[I]); int_pic_2[I] = trans2(int_pic_1[I]); sem_post(event_2_3[I]); }
Challenges with thread programming • Race condition: occurs when multiple threads and write to the same memory location. • Solutionwith a coordination mechanism: lock, conditional variable, semaphore, etc • Coordination results in waiting among threads • Deadlocks: occur when threads are waiting for resources with circular dependencies
Deadlock example T1: T2: … … Lock(printer) lock(keyboard) … … Lock (keyboard) lock(printer) … … Unlock(keyboard) unlock(printer) … … Unlock(printer) unlock(keyboard)
Deadlock and third party software • In sequence programs, using third party software is trivial. • Call “system(“/usr/bin/ls”); • No knowledge about /usr/bin/ls is needed. • Could deadlock happen in thread programming by calling a third party program? • This is a big problem facing multi-thread programming.
Summary • What is thread coordination (synchronization)? Why? • What are the common thread coordinations? • Pthread’s support for thread coordination. • Mutex lock • Condition variable • Deadlock • Thread programming issues