A thread is a basic unit of CPU utilization within a process Each thread has its own – thread ID – program counter – register set – stack It shares the following with other threads within the same process – code section – data section – the heap (dynamically allocated memory) – open files A multi-threaded process can do multiple things at once A thread remains attached to his process
Economy: – Creating a thread is cheap Much cheaper than creating a process – Context-switching between threads is cheap Much cheaper than between processes Resource Sharing: – Threads naturally share memory With processes we have to use complicated IPC – Having concurrent activities in the same address space is very powerful
If one thread fails, then the process fails. – Not the case with processes Threads are more memory-constrained than processes – Due to OS limitation of the address space size of a single process
Use man to understand the following commands top (like task manager in Windows) ps (list processes) Use ps –uM to see processes with the associated threads.
Typical challenges of multi-threaded programming – Dividing activities among threads – Balancing load among threads – Split data among threads – Deal with data dependency and synchronization – Testing and Debugging
Threads can be supported in: User Space – The Kernel knows nothing of the existence of multiple threads – Threads are managed by some user-level thread library Ex: Java Green Threads Kernel Space – The kernel has data structure and functionality to deal with threads – Most modern OS support kernel threads
Different models can be used to support threading in kernel mode: – Many to one model – One to one model – Many to many model – Two level model We are talking here about the relation between the OS and the threads.
Many to one Advantage: multi-threading is efficient and low-overhead – No syscall to the kernel Major Drawback : if one threads blocks, then do all the others!
One to one Removes drawback of the Many-to-One Model Creating a new threads requires work by the kernel – Not as fast as in the Many-to-One Model Example: – Linux – Windows
Two level Uses both one to one and many to many The user choose Example: – IRIX, HP-UX, Tru64 UNIX – Solaris 8 and earlier
Thread libraries provide users with ways to create threads in their own programs – In C/C++: Pthreads Implemented by the kernel – In Java: Java Threads Implemented by the JVM, which relies on threads implemented by the kernel We will talk only about Pthreads
Pthreads is a standard set of C library functions for multithreaded programming – IEEE Portable Operating System Interface, POSIX, section standard, 1995 Pthread Library (60+ functions) – Thread management: create, exit, detach, join,... – Tools for synchronization,... Programs must include the file pthread.h Programs must be linked with the pthread library (-lpthread) gcc -lpthread test.o –o test
pthread_create() Creates a new thread int pthread_create ( pthread_t *thread, pthread_attr_t *attr, void * (*start_routine) (void *), void *arg); Return 0 for success otherwise an error code output argument for the id of the new thread input argument that specifies the attributes of the thread to be created (NULL = default attributes) argument to pass to the new thread routine. If the thread routine requires multiple arguments, they must be passed bundled up in an array or a structure
pthread_create() Creates a new thread int pthread_create ( pthread_t *thread, pthread_attr_t *attr, void * (*start_routine) (void *), void *arg); start_routine: function to use as the start of the new thread
Objective: Create a thread to compute the sum of the elements of an array. Step by step design… Needs three arguments – the array – its size – where to store the sum typdef struct { int array[5]; int size; int sum; } arguments;
#include typedef struct { int array[5]; int size; int sum; } arguments; void *compute(void *arg) { int i; arguments *str=(arguments *)arg; str->sum=0; for (i = 0; i size; i++) str->sum += str->array[i]; printf (" The sum is : %d \n",str->sum); return; } int main () { pthread_t wt; arguments arg; int i; for (i=0; i<5; i++) arg.array[i] = i; arg.size = 5; pthread_create(&wt, NULL, compute, &arg); return 0; }
#include typedef struct { int array[5]; int size; int sum; } arguments; void *compute(void *arg) { int i; arguments *str=(arguments *)arg; str->sum=0; for (i = 0; i size; i++) str->sum += str->array[i]; printf (" The sum is : %d \n",str->sum); while(1); return; } int main () { pthread_t wt; arguments arg; int i; for (i=0; i<5; i++) arg.array[i] = i; arg.size = 5; pthread_create(&wt, NULL, compute, &arg); return 0; } What will ps –uM show after running this program? Why?
#include typedef struct { int array[5]; int size; int sum; } arguments; void *compute(void *arg) { int i; arguments *str=(arguments *)arg; str->sum=0; for (i = 0; i size; i++) str->sum += str->array[i]; printf (" The sum is : %d \n",str->sum); while(1); return; } int main () { pthread_t wt; arguments arg; int i; for (i=0; i<5; i++) arg.array[i] = i; arg.size = 5; pthread_create(&wt, NULL, compute, &arg); while(1); return 0; } What about now?
#include typedef struct { int array[5]; int size; int sum; } arguments; void *compute(void *arg) { int i; arguments *str=(arguments *)arg; str->sum=0; for (i = 0; i size; i++) str->sum += str->array[i]; printf (" The sum is : %d \n",str->sum); return; } int main () { pthread_t wt; arguments arg; int i; for (i=0; i<5; i++) arg.array[i] = i; arg.size = 5; pthread_create(&wt, NULL, compute, &arg); printf(“SUM= %d \n”,arg.sum); return 0; } What will be printed?
#include typedef struct { int array[5]; int size; int sum; } arguments; void *compute(void *arg) { int i; arguments *str=(arguments *)arg; str->sum=0; sleep(1); for (i = 0; i size; i++) str->sum += str->array[i]; printf (" The sum is : %d \n",str->sum); return; } int main () { pthread_t wt; arguments arg; int i; for (i=0; i<5; i++) arg.array[i] = i; arg.size = 5; pthread_create(&wt, NULL, compute, &arg); printf(“SUM= %d \n”,arg.sum); return 0; } What will be printed now?
The main thread continues its normal execution after creating the child thread If the main thread terminates, then all threads are killed! – Challenging questions! Memory is shared by the parent and the child – nothing prevents the parent from modifying the memory (structure) while the child is still executing – which may lead to a wrong computation – we need to have synchronization mechanisms After creating the child thread, the main thread continue. If it terminates before the completion of the child thread, the child will be killed. – Challenging questions!
pthread_exit(): Terminates the calling thread. void pthread_exit(void *retval); – The return value is made available to another thread calling a pthread_join() – If a thread returns, the call to pthread_exit() is implicit
pthread_join(): Causes the calling thread to wait for another thread to terminate int pthread_join(pthread_t thread, void **value_ptr); Return 0 for success otherwise an error code The id of the thread to wait for output parameter, value given to pthread_exit()
pthread_kill() Causes the termination of a thread int pthread_kill(pthread_t thread, int sig); Return 0 for success otherwise an error code The id of the thread to terminate