Pthread (continue) • General pthread program structure

advertisement
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: Dumb
Robot: Dumber
Motivating Example: Too Much Milk
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
Dumber
4:15 Head for the
warehouse
Arrive with milk
Motivating Example: Too Much Milk
Dumb
4:20
Dumber
4:15 Head for the
warehouse
Arrive with milk
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…”
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
…
X = 400
…
T2
…
Y = X+1
...
T1
…
X = 400
X ready
…
…
T2
…
….
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
Producer consumer program
without condition variables
/* 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();
}
Producer consumer with
condition variables
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.
– Solution with 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:
…
Lock(printer)
…
Lock (keyboard)
…
Unlock(keyboard)
…
Unlock(printer)
T2:
…
lock(keyboard)
…
lock(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
Download