0% found this document useful (0 votes)
59 views20 pages

Thread Creation

Thread synchronization is important when threads share access to variables. The document discusses three common synchronization primitives: mutex locks, condition variables, and semaphores. Mutex locks allow only one thread to access a critical section of code at a time to prevent race conditions on shared data. Condition variables allow threads to block until a condition is met, such as a buffer becoming full or empty. Semaphores permit a limited number of threads to access a section of code simultaneously.

Uploaded by

Stelios Evagorou
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
59 views20 pages

Thread Creation

Thread synchronization is important when threads share access to variables. The document discusses three common synchronization primitives: mutex locks, condition variables, and semaphores. Mutex locks allow only one thread to access a critical section of code at a time to prevent race conditions on shared data. Condition variables allow threads to block until a condition is met, such as a buffer becoming full or empty. Semaphores permit a limited number of threads to access a section of code simultaneously.

Uploaded by

Stelios Evagorou
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 20

EPL222 – Lab4

 Most of threaded programs have threads that


interact with one another
◦ Interaction in the form of sharing access to variables
 Multiple concurrent reads (ok)
 Multiple concurrent writes (not ok, outcome non-
deterministic)
 One write, multiple reads (not ok, outcome non-
deterministic)
◦ Need to make sure that the outcome is deterministic
 Synchronization: allowing concurrent accesses to variables,
removing non-deterministic outcome by enforcing some
order during thread execution
 Three basic synchronization primitives
◦ mutex locks
◦ condition variables
◦ semaphores

EPL222 - Labs 06/02/2020 2


 Mutual exclusion (mutex):
◦ guard against multiple threads modifying the same
shared data simultaneously
◦ provides locking/unlocking critical code sections
where shared data is modified
◦ each thread waits for the mutex to be unlocked (by
the thread who locked it) before performing the
code section
Thread 1: Thread 2:
insert A to tree insert B to tree

Thread 1: Thread 2:
lock(tree) lock(tree)
insert A to tree insert A to tree
unlock(tree) unlock(tree)
EPL222 - Labs 06/02/2020 3
 A typical sequence in the use of a mutex is as
follows:
◦ Create and initialize a mutex variable
◦ Several threads attempt to lock the mutex
◦ Only one succeeds and that thread owns the mutex
◦ The owner thread performs some set of actions
◦ The owner unlocks the mutex
◦ Another thread acquires the mutex and repeats the
process
◦ Finally the mutex is destroyed

EPL222 - Labs 06/02/2020 4


int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);

 a new data type named pthread_mutex_t is designated for


mutexes
 a mutex is like a key (to access the code section) that is
handed to only one thread at a time
◦ if multiple threads try to gain lock at the same time, the return
order is based on priority of the threads
◦ higher priorities return first
◦ no guarantees about ordering between same priority threads
 the attribute of a mutex can be controlled by using the
pthread_mutex_init() function
 the lock/unlock functions work in tandem

EPL222 - Labs 06/02/2020 5


#include <pthread.h>
...
Whenever a thread
reaches the
pthread_mutex_t my_mutex;
...
int main(){ lock/unlock block, it
int tmp; first determines if the
... mutex is locked.
// initialize the mutex
tmp = pthread_mutex_init(&my_mutex, NULL );
If so, it waits until it is
...
// create threads unlocked.
...
pthread_mutex_lock(&my_mutex ); Otherwise, it takes
do_something_private(); the mutex and locks
pthread_mutex_unlock(&my_mutex ); it, unlocking it when
it's done.
...
pthread_mutex_destroy(&my_mutex );
return 0;
}

EPL222 - Labs 06/02/2020 6


#include <stdio.h> void consumer(char* buf) {
#include <pthread.h> for(;;) {
while(count == 0);
#define MAX_SIZE 5 pthread_mutex_lock(bLock);
pthread_mutex_t bLock; useChar(buf[count-1]);
int count; count--;
pthread_mutex_unlock(bLock);
void producer(char* buf) { }
for(;;) { }
while(count == MAX_SIZE);
int main() {
pthread_mutex_lock(bLock);
char buffer[MAX_SIZE];
buf[count] = getChar();
pthread_t p;
count++; count = 0;
pthread_mutex_unlock(bLock); pthread_mutex_init(&bLock);
} pthread_create(&p, NULL,
} (void*)producer, &buffer);
consume(&buffer);
return 0;
}
EPL222 - Labs 06/02/2020 7
 Notice in the previous example a spin-lock
was used wait for a condition to be true
◦ the buffer to be full or empty
◦ spin-locks require CPU time to run
 waste of cycles
 Condition variables allow a thread to block
until a specific condition becomes true
◦ recall that a blocked process cannot be run
 doesn’t waste CPU cycles
◦ blocked thread goes to wait queue for condition
 When the condition becomes true, some
other thread signals the blocked thread(s)

EPL222 - Labs 06/02/2020 8


 A condition variable is created like a normal
variable
pthread_cond_t cv;
◦ must be initialized before being used
◦ can only be initialized once

int pthread_cond_init(pthread_cond_t *cv,


const pthread_condattr_t *cvattr);
◦ cv: a pointer to the condition variable to be initialized
◦ cvattr: attributes of the condition variable – usually
NULL

EPL222 - Labs 06/02/2020 9


int pthread_cond_wait(pthread_cond_t *cv,
pthread_mutex_t *mutex);

 A wait call is used to block a thread on a CV


 Blocks the thread until the specific condition is
signalled
◦ even after signal, condition may still not be true!
 Should be called with mutex locked
◦ the mutex is automatically released by the wait call
◦ the mutex is automatically reclaimed on return from wait
call (condition is signaled)
 cv: condition variable to block on
 mutex: the mutex to release while waiting
EPL222 - Labs 06/02/2020 10
int pthread_cond_signal(pthread_cond_t *cv);

 A signal call is used to “wake up” a single thread


waiting on a condition variable
◦ multiple threads may be waiting and there is no guarantee
as to which one wakes up first
◦ thread to wake up does not actually wake until the lock
indicated by the wait call becomes available
◦ condition thread was waiting for may not be true when the
thread actually gets to run again
 should always do a wait call inside of a while loop
 Called after mutex is locked, and must unlock
mutex after
 cv: condition variable to signal on

EPL222 - Labs 06/02/2020 11


#include <stdio.h> void consumer(char* buf) {
for(;;) {
#include <pthread.h>
pthread_mutex_lock(lock);
while(count == 0)
#define MAX_SIZE 5
pthread_cond_wait(notEmpty, lock);
pthread_mutex_t lock;
useChar(buf[count-1]);
pthread_cond_t notFull, notEmpty;
count--;
int count;
pthread_cond_signal(notFull);
pthread_mutex_unlock(lock);
void producer(char* buf) {
}
for(;;) {
}
pthreads_mutex_lock(lock);
while(count == MAX_SIZE)
int main() {
pthread_cond_wait(notFull, lock); char buf[MAX_SIZE];
buf[count] = getChar(); pthread_t p;
count++; count = 0;
pthread_cond_signal(notEmpty); pthread_mutex_init(&bufLock);
pthread_mutex_unlock(lock); pthread_cond_init(&notFull);
} pthread_cond_init(&notEmpty);
} pthread_create(&p, NULL, (void*)producer,
&buf);
consume(&buffer);
return 0;
}
EPL222 - Labs 06/02/2020 12
 The previous example only wakes a single thread
◦ not much control over which thread this is
 Perhaps all threads waiting on a condition need
to be woken up
◦ can do a broadcast of a signal
◦ very similar to a regular signal in every other respect

int pthread_cond_broadcast(pthread_cond_t *cv);


 cv: condition variable to signal all waiters on

EPL222 - Labs 06/02/2020 13


 Permit a limited number of threads to
execute a section of the code
 Similar to mutexes
 should include the <semaphore.h> header
file
 Semaphore functions do not have pthread_
prefixes; instead, they have sem_ prefixes

EPL222 - Labs 06/02/2020 14


 pthreads allows the specific creation of
semaphores
◦ can do increments and decrements of semaphore
value
◦ semaphore can be initialized to any value
◦ thread blocks if semaphore value is less than or
equal to zero when a decrement is attempted
◦ as soon as semaphore value is greater than zero,
one of the blocked threads wakes up and continues
 no guarantees as to which thread this might be

EPL222 - Labs 06/02/2020 15


sem_t sem;
◦ Semaphores are created like other variables

int sem_init(sem_t *sem, int pshared,


unsigned int value);
◦ initializes a semaphore object pointed to by sem
◦ pshared is a sharing option; a value of 0 means the
semaphore is local to the calling process
◦ gives an initial value (value) to the semaphore

int sem_destroy(sem_t *sem);


◦ frees the resources allocated to the semaphore sem
◦ usually called after pthread_join()
◦ an error will occur if a semaphore is destroyed for which a
thread is waiting

EPL222 - Labs 06/02/2020 16


int sem_post(sem_t *sem);
◦ Atomically increases the value of a semaphore by 1, i.e.,
when 2 threads call sem_post simultaneously, the
semaphore's value will also be increased by 2 (there are 2
atoms calling)
◦ if any threads are blocked on the semaphore, they will be
unblocked
◦ sem: the semaphore to increment

int sem_wait(sem_t *sem);


◦ Atomically decreases the value of a semaphore by 1; but
always waits until the semaphore has a non-zero value first
◦ If the semaphore value is greater than 0, the sem_wait call
return immediately
 otherwise it blocks the calling thread until the value becomes
greater than 0
◦ sem: semaphore to try and decrement

EPL222 - Labs 06/02/2020 17


#include <pthread.h>
#include <semaphore.h> void *thread_function( void *arg ){
...
void *thread_function( void *arg );
sem_wait( &semaphore );
... perform_task_when_sem_open();
// global variable just like mutexes ...
sem_t semaphore; pthread_exit(NULL);
... }
int main() {
int tmp;
...
// initialize the semaphore
tmp = sem_init( &semaphore, 0, 0 );
...
// create threads
pthread_create( &thread[i], NULL, thread_function, NULL );
...
while ( still_has_something_to_do() ) {
sem_post( &semaphore );
...
}
...
pthread_join( thread[i], NULL );
sem_destroy( &semaphore );
return 0;
}
EPL222 - Labs 06/02/2020 18
 The main thread increments the semaphore's
count value in the while loop
 The threads wait until the semaphore's count
value is non-zero before performing
perform_task_when_sem_open() and further
 Child thread activities stop only when
pthread_join() is called

EPL222 - Labs 06/02/2020 19


#include <stdio.h> void consumer(char* buf) {
#include <semaphore.h> int out = 0;
for(;;) {
#define MAX_SIZE 5 sem_wait(&full);
sem_t empty, full; useChar(buf[out]);
out = (out + 1) % MAX_SIZE;
void producer(char* buf) { sem_post(&empty);
int in = 0; }
for(;;) { }
sem_wait(&empty);
buf[in] = getChar(); int main() {
in = (in + 1) % MAX_SIZE; char buffer[MAX_SIZE];
sem_post(&full); pthread_t p;
} sem_init(&empty, 0, MAX_SIZE);
} sem_init(&full, 0, 0);
pthread_create(&p, NULL,
(void*)producer, &buffer);
consume(&buffer);
return 0;
}

EPL222 - Labs 06/02/2020 20

You might also like