Paralleland Distributed Programming Concurrency
Concurrency problems race condition synchronization hardware (eg matrix PCs) software (barrier, critical section, atomic operations) mutual exclusion critical section critical section in and out protocols solutions to issues of synchronization features: desirable vitality, safety, integrity possible errors deadlock, starvation
Mutual exclusions Locks (signaller, barriers) int lock=0; thread_procedure() { while (lock!= 0) {} // (busy wait) lock = 1; critical_section(); lock = 0; } Problems: busy waiting consumes computer resources procedure is not safe and does not povide vitality
Mutual exclusions How to solve problems with the input and output protocols for critical section: more complex algorithms increase the number of variables, increase data records and readings hardware support appropriate processor instructions system support system procedures implemented using hardware support appropriate API implementation with the use of system procedures
Mutual exclusions Semaphores: theoretical construction that allows the correct solution to the mutual exclusion problem semaphore is a global variable, on which can be made two indivisible, mutually exclusive operations, usually called P (probeer, wait) and V (verhoog, signal) (both often are implemented in the kernel of the operating system, because they contain atomic operations) P (int s) { if (s> 0) s--; else suspend_thread(); } V (int s) { if (somebody_sleeps()) wake_thread(); else s++; } initiation of the semaphore, such as init (int s, int v) {s = v;} implementation of V determines the integrity of the semaphore (eg. FIFO) value of s is the number of accesses to the resource - for example, a binary semaphore.
Concurrency problems The problem of producers and consumers: One group of threads produce data, the second group consume it - how to ensure efficient (no jams and starvation) progress of the procedure The problem of readers and writers similarly as above, except that production (write) can be done at the same time only by one process, but eating (read) is done by a lot of processes at once
Concurrency problems Dining philosophers problem: philosopher: either eat, or think philosophers are sitting at the table, in front of each lies a plate between each pair of plates lies a fork at the center of the table is a bowl of spaghetti problem is that for eating spaghetti two forks are needed (on both sides of the plate) how to ensure the survival of the philosophers?
Mutual exclusions Semaphores - tackling the dining philosophers problem easy solution with allow blocking (incorrect) fork[i], i=0..4 // five binary semaphores for five forks (initiated with 1) philosopher_thread(int i) //procedure for i-th philosopher five { concurrent threads i=0..4 for(;;) { think(); wait(fork[i]); wait(fork[(i+1) mod 5]); eat(); signal(fork[i]); signal(fork[(i+1) mod 5]); } } When will be lockdown?
Mutual exclusions Semaphores - tackling the dining philosophers problem correct solution fork[i], i=0..4 // five binary semaphores for five forks (initiated with 1) permission = 4; // quadruple semaphore with initaial value = 4 philosopher_thread(int i) //procedure for i-th philosopher five { concurrent threads i=0..4 for(;;) { think(); wait(permission); wait(fork[i]); wait(fork[(i+1) mod 5]); eat(); signal(fork[i]); signal(fork[(i+1) mod 5]); signal(permission); } }
Mutex The POSIX specification: mutex - mutual exclusion creation of mutex int pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutex attr_t *mutexattr) locking (there is a version pthread_mutex_trylock) int pthread_mutex_lock (pthread_mutex_t *mutex) opening int pthread_mutex_unlock (pthread_mutex_t *mutex) responsibility for the proper use of mutexes (that guarantee safety and vitality) rests on the programmer
Conditionvariables POSIX implementation of semaphores A condition variable is always used in conjunction with a mutex lock Basic procedures: Initialization: int pthread_cond_init (pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); pthread_cond_t cond = PTHREAD_COND_INITIALIZER; Communication: int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); int pthread_cond_broadcast(pthread_cond_t *cond); int pthread_cond_signal(pthread_cond_t *cond);
Conditionvariables Main Thread o Declare and initialize global data/variables which require synchronization (such as "count") o Declare and initialize a condition variable object o Declare and initialize an associated mutex o Create threads A and B to do work Thread A o Do work up to the point where a certain condition must occur (such as "count" must reach a specified Thread B o o value) o o Lock associated mutex and check value of a global variable o o Call pthread_cond_wait() to perform a blocking wait for signal from Thread-B. Note that a o call to pthread_cond_wait()automatically and o atomically unlocks the associated mutex variable so that it can be used by Thread-B. o When signalled, wake up. Mutex is automatically and atomically locked. o Explicitly unlock mutex o Continue Main Thread Join / Continue Do work Lock associated mutex Change the value of the global variable that Thread-A is waiting upon. Check value of the global Thread-A wait variable. If it fulfills the desired condition, signal Thread-A. Unlock mutex. Continue