280 likes | 292 Views
Learn to write portable, efficient multi-threaded applications by leveraging POSIX threads, compatible with various Unix systems and OS X, understanding thread creation, termination, data passing, reentrant code, and thread synchronization.
E N D
operating systems Unix Threads
operating systems User Thread Packages pthread package mach c-threads Sun Solaris3 UI threads Kernel Threads Windows NT, XP
operating systems Pthreads The solution to writing portable, multi-threaded applications is to use POSIX threads. POSIX threads work on Linux, FreeBSD, Solaris, AIX, and other *nix systems. OS/X uses pthreads. They are not shipped with Windows, but pthread packages are available from other vendors.
operating systems Posix Thread Support Prior to POSIX 1003.1c, there was no standard interface for threads. Each vendor, if they supported threads at all, had their own threads package. POSIX standardized threads in POSIX1003.1c, in June of 1995 * Posix Threads are implemented as kernel threads on OS X
operating systems Reentrant code When using threads, you must take care that any code you use is reentrant. Reentrant code can be called more than once, perhaps from different threads, without conflicts. Each copy of the code must have its own copy of any variables that it uses, so must not use global or static variables Be careful when using threads, because some system calls are not thread-safe.
operating systems Creating a New Thread #include <pthread.h> int pthread_create(pthread_t *thread, pthread_att *attr, void *(*start_routine)(void *), void *arg); the thread handle gets stored here thread attributes, usually NULL. returns a 0 if successful otherwise an errno! Note that this differs from the normal Unix standard of returning -1 for an error. argument given to the thread function. usually a pointer to a struct or an array the function to begin executing on this thread. It must take a void * as an argument and return a void *. When the function returns, the thread exits.
operating systems A thread exits when the thread function returns. Alternatively, the thread can call the pthread_exit function. Terminating a Thread #include <pthread.h> void pthread_exit(void *retval); this must point to data that exists after the thread terminates! Never point to local data defined in the thread!
#include <pthread.h> #include <stdio.h> void* printxs (void* unused) // the thread function { while(1) fputc('x', stderr); return NULL; } int main ( ) { pthread_t x_thread; // the thread handle pthread_create (&x_thread, NULL, &printxs, NULL); while(1) fputc('o', stderr); return 0; } operating systems Very Simple Thread Program pthread_create main thread of execution printxs thread1
operating systems Passing Data to Threads The thread argument is of type void* This allows us to pass a lot of data to a thread by passing a pointer to a struct or an array of data.
Data Passing Example #include <pthread.h> #include <stdio.h> // use this struct to pass parameters to thread function struct char_print_params { char print_char; int number_to_print; } we will declare this structure to pass multiple pieces of data to a thread function. Each thread can have different values.
cast the pointer passed to the thread function to a pointer to a char_print_params structure, then use the structure to get at the parameters. // the print function … void* char_print (void* params) { struct char_print_params* p; p = (struct char_print_params*) params; int i; for (i = 0; i < p->number_to_print; i++) fputc (p->print_char, stderr); return NULL; }
int main ( ) { pthread_t thread1_handle; pthread_t thread2_handle; struct char_print_params thread1_args; struct char_print_params thread2_args; thread1_args.print_char = ‘x’; thread1_args.number_to_print = 3000; pthread_create (&thread1_handle, NULL, &char_print, &thread1_args); thread2_args.print_char = ‘y’; thread2_args.number_to_print = 5000; pthread_create (&thread2_handle, NULL, &char_print, &thread2_args); return 0; } pthread_create main thread print xs print ys
This program has a serious bug … do you know what it is? thread2.c
int main ( ) { pthread_t thread1_handle; pthread_t thread2_handle; struct char_print_params thread1_args; struct char_print_params thread2_args; thread1_args.print_char = ‘x’; thread1_args.number_to_print = 3000; pthread_create (&thread1_handle, NULL, &char_print, &thread1_args); thread2_args.print_char = ‘y’; thread2_args.number_to_print = 5000; pthread_create (&thread2_handle, NULL, &char_print, &thread2_args); return 0; } main will likely exit before either thread finishes. But notice that the thread arguments are pointers to structures declared as local variables in main. When main exits, these pointers are no longer valid!
One solution is to force main to wait until the other two threads are done.
Waiting for a Thread #include <pthread.h> int pthread_join(pthread_t th, void **retval); 0 on success or error code. pointer to the return value. NULL if there is nothing to return. the thread to wait for Note that the thread that started the new thread does not necessarily have to be the thread that executes the pthread_join() function.
int main ( ) { pthread_t thread1_handle; pthread_t thread2_handle; struct char_print_params thread1_args; struct char_print_params thread2_args; thread1_args.print_char = ‘x’; thread1_args.number_to_print = 3000; pthread_create (&thread1_handle, NULL, &char_print, &thread1_args); thread2_args.print_char = ‘y’; thread2_args.number_to_print = 5000; pthread_create (&thread2_handle, NULL, &char_print, &thread2_args); pthread_join (thread1_handle, NULL); pthread_join (thread2_handle, NULL); return 0; } no return values thread3.c
void *thread_function(void *arg); char message[ ] = “Hello World!”; int main () { int res; // to hold return values pthread_t a_thread; // to keep our thread handle void *thread_result; // to store the return value from the thread Exploiting Shared Memory thread4.c
the address of the thread function thread handle res = pthread_create(&a_thread, NULL, &thread_function, (void *)message); if (res != 0) { perror(“Thread creation failed”); exit(1); } cast to a void* and pass message to the thread function at this point, two threads are running, if the thread creation step was successful. Each thread now gets its own share of time slices.
printf(“Waiting for the thread to finish…\n”); res = pthread_join(a_thread, &thread_result); if( res != 0) { perror(“Thread join failed”); exit(1); } the return value is stored here the pthread_join function puts this thread in a wait state until the thread we just started (a_thread is its handle) finishes. z z z z z z z z
now while the first thread is waiting … void thread_function(void *arg) { printf(“thread function running. Argument was %s\n”, (char *)arg); sleep(3); strcpy(message, “Bye!”); pthread_exit(“Thank you for the CPU time”); } print out the argument passed “Hello world!” // sleep for a while change the message. We share the same address space! Could not do this if we used a fork( ). exit, returning this message to the caller z z z z z z z z
printf(“Thread_join returned, value returned = %s\n”, (char *)thread_result); printf(“Value of message is now %s\n”, message); exit(0); }
Thread Ids Occasionally, it is useful for a sequence of code to determine which thread is running it. pthread_t pthread_self ( );
Compare two thread IDs with the pthread_equal function. For example, you might check to make sure that a thread ID is not equal to the current thread’s ID, since a thread cannot join itself. Comparing Thread IDs if (!pthread_equal (pthread_self( ), other_thread)) pthread_join (other_thread, NULL);
Thread attributes allow the programmer to fine tune the behavior of individual threads. Thread Attributes Initialization Stack Size Stack Address Detach State Scope Inheritance Schedule Policy Schedule Parameters
Detaching a Thread When a thread exits, it does not release its resources unless it is a detached thread, or another thread is waiting with a pthread_join( ).
int pthread_detach(pthread_t thread); if successful returns 0 This thread will release all of its resources when it exits. This thread will not report any status when it exits. This thread is not joinable.