Presentation is loading. Please wait.

Presentation is loading. Please wait.

POSIX Threads 1. Background 2. Threads vs. Processes

Similar presentations


Presentation on theme: "POSIX Threads 1. Background 2. Threads vs. Processes"— Presentation transcript:

1 POSIX Threads 1. Background 2. Threads vs. Processes
3. Thread Synchronization 4. Mutex Variables 5. Condition Variables 6. Threads and UNIX

2 1. Background Threads are light-weight processes
only local variables in a function are specific to a thread (e.g. each thread has its own stack) most other data is shared between threads (e.g. global variables & the heap) pthreads is the POSIX threads standard. The associated library interface is obtained by: #include <pthread.h>

3 1.1. Reasons for Using Threads
Efficiency Ease of data sharing Common uses of parallelism: overlapping I/O do long I/O and CPU tasks at the same time asynchronous events wait for a response and do something else real-time scheduling quickly respond to important tasks to utilize multiple processors

4 2. Threads vs Processes A thread uses less system resources than a similar process (for the same task). A thread may require more user space resources than a similar process depends on the implementation Threads (within the same process) share everything except their stack and processor state continued

5 The threads mechanism can be used instead of many different process-based mechanisms:
non-blocking I/O, shared memory (IPC), jmp() functions, signals, ... Threads use an inherently simpler shared memory mechanism than processes: inter-thread communication is far easier but it is also much easier to code parallelism errors (e.g. race conditions)

6 Problems with Threads Unpredictable functioning (non-determinism)
Difficult to debug Impossible to prove correct Almost impossible to test thoroughly Specific behavior not easily reproducible Simple conceptually, but added complexities for dealing with many pitfalls

7 2.1. Processes Example shared memory Global variables, etc
shmid, shm_ptr, r1p, r2p shmid, shm_ptr, r1p, r2p r1p r2p child 1: do_one_thing() child 2: do_another_thing()

8 2.1. ints_processes.c continued
#include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <errno.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> void do_one_thing(int *pnum); void do_another_thing(int *pnum); int shmid, *shm_ptr; /* global, but not shared */ int *r1p, *r2p; : : continued

9 int main() { pid_t child1, child2; /. initialize shared memory
int main() { pid_t child1, child2; /* initialize shared memory */ shmid = shmget(IPC_PRIVATE, *sizeof(int), (IPC_CREAT | 0666)); if ((shm_ptr = (int *)shmat(shmid, (char *)0, 0)) < 0 ) perror("shmat failed"); : continued

10 /* initialise shared memory ints */ r1p = shm_ptr; r2p = (shm_ptr + 1); *r1p = 0; *r2p = 0; /* start forking children */ if ((child1 = fork()) == 0) { /* first child */ do_one_thing(r1p); exit(0); } : continued

11 if ((child2 = fork()) == 0) {. /. second child
if ((child2 = fork()) == 0) { /* second child */ do_another_thing(r2p); exit(0); } /* parent waits for children */ wait(NULL); wait(NULL); printf(“Final Values: %d, %d\n”, *r1p, *r2p); return 0; } continued

12 void do_one_thing(int. pnum) /. Waste time, and increment pnum
void do_one_thing(int *pnum) /* Waste time, and increment pnum */ { int i, j, x=0; for (i = 0; i < 4; i++) { printf("doing one thing\n"); for (j = 0; j < 10000; j++) x = x + i; (*pnum)++; } } continued

13 void do_another_thing(int. pnum) /. Waste time, and increment pnum
void do_another_thing(int *pnum) /* Waste time, and increment pnum. The code is almost the same as do_one_thing() */ { int i, j, x=0; for (i = 0; i < 4; i++) { printf("doing another \n"); for (j = 0; j < 10000; j++) x = x + i; (*pnum)++; } } continued

14 Usage $ ints_processes doing one thing doing one thing doing another doing another doing another doing another doing one thing doing one thing Final Values: 4, 4 $ Order may vary each time ints_processes is executed.

15 2.2. Threads Version Global variables, etc r1 shared by default r2
thread 1: do_one_thing() thread 2: do_another_thing()

16 2.2. ints_threads.c #include <stdio.h>
#include <pthread.h> /* Same functions as in simple_processes.c */ void do_one_thing(int *pnum); void do_another_thing(int *pnum); /* Global (and shared) integers */ int r1 = 0, r2 = 0; : continued

17 int main() { pthread_t thread1, thread2; pthread_create(&thread1, NULL, (void *) do_one_thing, (void *) &r1); pthread_create(&thread2, NULL, (void *) do_another_thing, (void *) &r2); pthread_join(thread1, NULL); pthread_join(thread2, NULL); printf(“Final Values: %d, %d\n”,r1, r2); return 0; }

18 Notes No need for shared memory library functions Difficult to program
threads approach has better performance And simpler code Difficult to program synchronization problems (with processes, too) ALL Global variables are accessible to all threads easy to make coding mistakes

19 2.3. Thread Functions int pthread_create(pthread_t *thread, const pthread_attr_t attr, void *(*func)(void *), void *arg); Create a new thread with attributes specified in attr (usually NULL). Start executing func() and pass it arg. Consider carefully what to pass as the argument Return 0 if ok, non-zero if error. continued

20 int pthread_join(pthread_t thread, void **value_ptr);
Make the calling thread wait for the specified thread to terminate. value_ptr is assigned its return value (or PTHREAD_CANCELLED).

21 2.4. Matrix Multiplication Example
Parallelize matrix multiplication: = *

22 Coding Approach Create MATSIZE (e.g. 4) threads: one for each column of the results[] array each column will be calculated in parallel The parallelism could be increased by creating MATSIZE*MATSIZE threads: one for each element of the results[] array.

23 matmult.c #include <stdio.h> #include <pthread.h> #define MATSIZE 4 void *matMult(void *); /* note the template */ void printMult(void); /* global and shared data */ int mat1[MATSIZE][MATSIZE] = {{9,8,7,6},{6,5,4,3},{3,2,1,0},{0,-1,-2,-3}}; int mat2[MATSIZE][MATSIZE] = {{1,2,3,4},{4,5,6,7},{7,8,9,10},{10,11,12,13}}; int result[MATSIZE][MATSIZE]; :

24 int main() { pthread_t thr[MATSIZE]; int i, *iPtr;
for(i=0; i<MATSIZE; i++) iPtr = (int*) malloc(sizeof(int)); *iPtr = i; pthread_create(&thr[i], NULL, matMult, (void *)iPtr); for(i=0; i < MATSIZE; i++) pthread_join(thr[i], NULL); printMult(); return 0; }

25 void *matMult(void *colvPtr) { int i, j; int col = *colvPtr;
free(colPtr); for(i=0; i < MATSIZE; i++) { result[i][col] = 0; for(j=0; j < MATSIZE; j++) result[i][col] += mat1[i][j] * mat2[j][col]; } return NULL; }

26 void printMult(void) { int i, j; for(i=0; i < MATSIZE; i++) { printf(“|”); for(j=0; j < MATSIZE; j++) printf(“%3d”, mat1[i][j]); printf(“|%c|”, (i==MATSIZE/2 ? ‘*’ : ‘‘)); for(j=0; j < MATSIZE; j++) printf(“%3d”, mat2[i][j]); printf(“|%c|”, (i==MATSIZE/2 ? ‘=’ : ‘‘)); for(j=0; j < MATSIZE; j++) printf(“%4d”, result[i][j]); printf(“|\n”); } }

27 3. Thread Synchronization
pthread_join() like wait() for processes mutex variables like binary semaphores for processes condition variables wait for an ‘event’ (e.g. a variable is assigned a certain value), which is ‘signaled’ by another thread.

28 4. Mutex Variables A mutex variable is a mutual exclusion lock, allowing threads to control access to shared data. Only one thread can hold a mutex at a time. The threads must agree to use the mutex to protect the shared data. Mutex variables are not managed by OS and thus very efficient (no system calls).

29 Example Diagram Global variables, etc r1 r3 r3_mutex shared by default
lock lock thread 1: do_one_thing() thread 2: do_another_thing()

30 ints_mutex.c #include <stdio.h> #include <stdlib.h> #include <pthread.h> void lock_one_thing(int *pnum); void lock_another_thing(int *pnum); /* global (and shared) variables */ int r1 = 0, r2 = 0, r3 = 0; pthread_mutex_t r3_mutex; : continued

31 void main(int argc, char
void main(int argc, char *argv[]) { pthread_t thread1, thread2; pthread_mutex_init(&r3_mutex, NULL); if (argc < 2) { printf("usage %s number\n",argv[0]); exit(1); } r3 = atoi(argv[1]); : continued

32 : pthread_create(&thread1, NULL,. (void. ) lock_one_thing,. (void
: pthread_create(&thread1, NULL, (void *) lock_one_thing, (void *) &r1); pthread_create(&thread2, NULL, (void *) lock_another_thing, (void *) &r2); pthread_join(thread1, NULL); pthread_join(thread2, NULL); printf(“Final Values: %d, %d\n”, r1, r2); } continued

33 void lock_one_thing(int
void lock_one_thing(int *pnum) { int i, j, x=0; for (i = 0; i < 4; i++) { pthread_mutex_lock(&r3_mutex); r3 = r3 + (*pnum); printf(“one altered r3: %d\n”, r3); pthread_mutex_unlock(&r3_mutex); for (j = 0; j < 10000; j++) x = x + i; (*pnum)++; } } continued

34 void lock_another_thing(int
void lock_another_thing(int *pnum) { int i, j, x=0; for (i = 0; i < 4; i++) { pthread_mutex_lock(&r3_mutex); r3 = r3 + (*pnum); printf(“another altered r3: %d\n”,r3); pthread_mutex_unlock(&r3_mutex); for (j = 0; j < 10000; j++) x = x + i; (*pnum)++; } } continued

35 Usage $ ints_mutex 4 one altered r3: 4 one altered r3: 5 one altered r3: 7 one altered r3: 10 another altered r3: 10 another altered r3: 11 another altered r3: 13 another altered r3: 16 Final Values: 4, 4 $

36 4.2. Mutex Functions int pthread_mutex_lock( pthread_mutex_t *mutex); Lock an unlocked mutex; if already locked, the thread waits until the mutex becomes unlocked. int pthread_mutex_trylock( pthread_mutex_t *mutex); Lock an unlocked mutex, but if locked, do not block and return EBUSY continued

37 int pthread_mutex_unlock( pthread_mutex_t *mutex);
Unlock a mutex; if any threads are waiting to lock this mutex, one is woken up.

38 5. Condition Variables Synchronize threads by using events
e.g. a variable is assigned a certain value A thread (or threads) wait for an event which is ‘signaled’ by another thread. These events are not UNIX signals The ‘signal’ causes the thread (or threads) to wake up. We won’t cover the details of using condition variables until next week.

39 6. Threads and UNIX UNIX was originally designed to handle processes before shared memory multiprocessors were available. how to add in threads to take advantage of multiple CPUs ? A process is a ‘container’ for one or more threads all the threads share the process’ memory address space

40 How do threads deal with...
signals? library functions? process management? e.g. fork(), exec()

41 6.1. Signals Each thread can have its own signal mask and signal actions. Signals can be sent to a specific thread or to the process that ‘holds’ the thread(s). Synchronous signals get delivered to the thread that caused them (such as SEGV or SIGFPE)

42 6.2. Library Functions How do several threads share the same library function at the same time? Any function that uses global variables (or statically declared variables) is suspect! Answer: thread-safe libraries Libraries can be made thread-safe by adding mutexes around the library function’s global variables library functions may not be thread-safe! Some libraries have thread-safe alternatives (ctime vs. ctime_r)

43 What if a thread is terminated inside a library function?
e.g. in the middle of changing global data Answer: the pthreads library includes cancellation-safe functions they clean up upon cancellation

44 What if a thread blocks inside a library function?
Answer: the pthreads library includes many functions which only block the thread, not the entire process the programmer can also turn off blocking in other functions

45 6.3. Process Management What does a fork() call from a thread do to the other threads in the containing process? Answer: the new child process contains a single copy of the thread that called fork() What happens to mutexes? big headaches are possible! Guidelines: Fork from a process with only one thread Fork before creating additional threads Fork only from the main (parent) thread Hold no locks during the fork

46 What does an exec() call from a thread do to the threads in the containing process?
Answer: all the threads terminate, and a new thread is created for the exec() program.


Download ppt "POSIX Threads 1. Background 2. Threads vs. Processes"

Similar presentations


Ads by Google