POSIX Threads
POSIX Threads
General information
- Thread operations include:
- creation
- termination
- synchronization (joins,blocking)
- scheduling
- data management
- process interaction.
- All threads within a process share the same address space.
- Threads in the same process share:
- Process instructions
- Most data
- open files (descriptors)
- signals and signal handlers
- current working directory
- User and group id
- Each thread has a unique:
- Thread ID
- set of registers, stack pointer
- stack for local variables, return addresses
- signal mask
- priority
- Return value: errno
Compiling programs that use POSIX threads
gcc -pthread -Wall source.c -o output
C functions for POSIX threads
- create a new thread:
pthread_create
- wait for termination of another thread:
pthread_join
- terminate the calling thread:
pthread_exit
- return identifier of current thread:
pthread_self
Example - creating threads
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
// define a constant for the number of threads
#define THR_COUNT 4
// This function will be executed by each thread
void* thread_function(void *param)
{
printf("I am thread %lu\n", (ulong)pthread_self());
return 0;
}
int main()
{
int i = 0;
pthread_t thr[THR_COUNT];
// create threads
for (i = 0; i < THR_COUNT; i++)
{
pthread_create(&thr[i], NULL, thread_function, NULL);
}
// wait for the threads to terminate
for (i = 0; i < THR_COUNT; i++)
{
pthread_join(thr[i], NULL);
}
return 0;
}
Example - sending parameters to threads
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
// define a constant for the number of threads
#define THR_COUNT 4
// This function will be executed by each thread
void* thread_function(void *param)
{
int nr = *(int*)param;
printf("I am thread %lu and I received the value %d\n", (ulong)pthread_self(), nr);
return 0;
}
int main()
{
int i = 0;
pthread_t thr[THR_COUNT];
int thr_param[THR_COUNT];
for (i = 0; i < THR_COUNT; i++)
{
// prepare the parameters
thr_param[i] = i;
// create the threads
pthread_create(&thr[i], NULL, thread_function, (void*)&thr_param[i]);
}
// wait for the threads to terminate
for (i = 0; i < THR_COUNT; i++)
{
pthread_join(thr[i], NULL);
}
return 0;
}
Example - Compute the sum of an array using threads (no synchronization)
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
// define a constant for the size of the array
#define ARR_SIZE 10000
// define a constant for the number of threads
#define THR_COUNT 4
// define a structure used to send parameters to the threads
typedef struct _THR_PARAM
{
int *array; // the array to process
int idx_start; // the start index in the array
int idx_end; // the end index in the array
}THR_PARAM;
// global variable - the sum of the elements in the array
int global_sum = 0;
// thread function - computes the sum of the elements of an array within a certain range
void* array_sum(void *param)
{
THR_PARAM thr_param;
int i;
// we will use a local variable to compute the sum
int local_sum = 0;
thr_param = *(THR_PARAM*)param;
// iterate the elements of the array
for (i = thr_param.idx_start; i < thr_param.idx_end; i++)
{
local_sum = local_sum + thr_param.array[i];
}
// add the sum computed by the thread to the global sum
global_sum = global_sum + local_sum;
return 0;
}
int main()
{
int i;
int array[ARR_SIZE];
pthread_t thr[THR_COUNT];
THR_PARAM thr_param[THR_COUNT];
// fill the array with 1s - easier to compute
for (i = 0; i < ARR_SIZE; i++)
{
array[i] = 1;
}
// prepare the params for each thread and create it
for (i = 0; i < THR_COUNT; i++)
{
// prepare the params for the thread: the array, the start index, the end index
thr_param[i].array = array;
thr_param[i].idx_start = (ARR_SIZE / THR_COUNT) * i;
thr_param[i].idx_end = (ARR_SIZE / THR_COUNT) * (i + 1);
// create the thread
pthread_create(&thr[i], NULL, array_sum, (void*)&thr_param[i]);
}
// wait for the threads to complete
for (i = 0; i < THR_COUNT; i++)
{
pthread_join(thr[i], NULL);
}
// display the sum
printf("Sum = %d\n", global_sum);
return 0;
}
Mutexes
- A synchronization mechanism that can be used to protect variables and prevent multiple threads from accessing them at the same time.
- Can be thought of as a sort of lock (RO: lacat)
- A thread obtains a mutex by calling the lock function. The thread can release the mutex by calling the unlock function.
- While a thread holds a mutex (the lock is locked), the other threads are blocked waiting for the mutex
- After a mutex is released the other threads will try to obtain the mutex
- Note: If they are not used wisely, they can "sabotage" the parallelism. If the mutex is not used in the right place the threads may to execute instructions in parallel, even if we use 4-8-10-100 threads.
Example - Compute the sum of an array using threads (with synchronization)
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
// define a constant for the size of the array
#define ARR_SIZE 10000
// define a constant for the number of threads
#define THR_COUNT 4
// define a structure used to send parameters to the threads
typedef struct _THR_PARAM
{
int *array; // the array to process
int idx_start; // the start index in the array
int idx_end; // the end index in the array
}THR_PARAM;
// global variable - the sum of the elements in the array
int global_sum = 0;
// global variable - the mutex used for synchronization
pthread_mutex_t mutex;
// thread function - computes the sum of the elements of an array within a certain range
void* array_sum(void *param)
{
THR_PARAM thr_param;
int i;
// we will use a local variable to compute the sum
int local_sum = 0;
thr_param = *(THR_PARAM*)param;
// iterate the elements of the array
for (i = thr_param.idx_start; i < thr_param.idx_end; i++)
{
local_sum = local_sum + thr_param.array[i];
}
// acquire the lock
pthread_mutex_lock(&mutex);
// the instructions in this area can be executed by a single thread at a time
// add the sum computed by the thread to the global sum
global_sum = global_sum + local_sum;
// release the lock
pthread_mutex_unlock(&mutex);
return 0;
}
int main()
{
int i;
int array[ARR_SIZE];
pthread_t thr[THR_COUNT];
THR_PARAM thr_param[THR_COUNT];
// initialize the mutex (prepare the lock)
pthread_mutex_init(&mutex, NULL);
// fill the array with 1s - easier to compute
for (i = 0; i < ARR_SIZE; i++)
{
array[i] = 1;
}
// prepare the params for each thread and create it
for (i = 0; i < THR_COUNT; i++)
{
// prepare the params for the thread: the array, the start index, the end index
thr_param[i].array = array;
thr_param[i].idx_start = (ARR_SIZE / THR_COUNT) * i;
thr_param[i].idx_end = (ARR_SIZE / THR_COUNT) * (i + 1);
// create the thread
pthread_create(&thr[i], NULL, array_sum, (void*)&thr_param[i]);
}
// wait for the threads to complete
for (i = 0; i < THR_COUNT; i++)
{
pthread_join(thr[i], NULL);
}
// destroy the mutex (discard the lock)
pthread_mutex_destroy(&mutex);
// display the sum
printf("Sum = %d\n", global_sum);
return 0;
}
Problems
- Write a program that creates 5 threads, giving each thread a string as parameter. Each thread will count and add to the global variables v and n as follows: the number of vowels contained by the string added to v, and the number of digits contained in the string added to n.
- A C program receives command line args numbers, and creates for each a thread that checks is the numbers are multiple of 2 or 5, incrementing a global variable. (use atoi)