290 likes | 431 Views
P-threads: create. #include < pthread.h > int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void * (*start)(void *), void *args);. tid: space used to return the thread id attr: thread attributes -- NULL start: a pointer to a function that takes a single
E N D
P-threads: create #include < pthread.h > int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void * (*start)(void *), void *args); • tid: space used to return the thread id • attr: thread attributes -- NULL • start: a pointer to a function that takes a single • argument of type void * and returns a value of type void * • args: a value to be passed as the argument to start • Return value: 0 means OK, non-zero indicates an error
P-threads: join&detach #include < pthread.h > int pthread_join(pthread_t tid, void **status); int pthread_detach(pthread_t tid); • pthread_join: wait for thread tid to finish • status: will be set to point to the space where the return value from the start function will be stored • pthread_detach: thread cannot be joined. System will • reclaim resources on thread_exit.
P-threads: example void *printme(void *){ printf("Hello world\n"); return NULL;} // could use pthread_exit(NULL) here main(){ pthread_t tid; void *status; if (pthread_create(&tid, NULL, printme, NULL) != 0) { perror("pthread_create”); exit(1); } if (pthread_join(tid, &status) != 0) { perror("pthread_join"); exit(1); } } Warning: tid is shared! What happens if you put a call to create in a loop?
P-threads: multiple threads void *printme(void *arg) { int me = *((int *)arg); printf(“Hello from %d\n”, me); return NULL; } int main(){ int i, j, vals[4]; pthread_t tids[4]; void *retval; for (i = 0; i < 4; i++) { vals[i] = i; pthread_create(tids+i, NULL, printme, (void *)(vals+i)); } for (j = 0; j < 4; j++) { printf("Trying to join with tid %d\n", i); pthread_join(tids[j], (void **) &retval); printf("Joined with tid %d\n", j); } }
P-threads: exit int main(){ int i, vals[4]; pthread_t tids[4]; for (i = 0; i < 4; i++) { vals[i] = i; pthread_create(tids+i, NULL, printme, (void *) (vals+i)); } return 0; } • Returning from astart function is equivalent to calling thread_exit • Returning from main is equivalent to calling exit!
Pthreads: Servicing multiple clients struct harg { int newfd; struct sockaddr_in; }; void *handler(void *arg) { struct harg *harg = (struct harg *)arg; pthread_detach(pthread_self()); ... } int main() { pthread_t tid1; int sockfd = openServer(atoi(argv[1])); int addrsize = sizeof(struct sockaddr_in); while (1) { struct harg *arg = (struct harg *)malloc(sizeof(*arg)); arg->newfd = accept(sockfd, (struct sockaddr *) &arg->addr, &addrsize); pthread_t tid; pthread_create(&tid, NULL, handler, (void *)arg); } }
P-threads: race condition example #define LEN 100 #define NUM_PARTS 4 #define PART_LEN LEN/NUM_PARTS void *sumPart(void *args) { int start = *((int *) args); int end = start + PART_LEN; int i; int mySum = 0; for (i = start; i <end; i++) { mySum += x[i]; } sum = sum + mySum; thread_exit(0); }
P-threads: race condition example int main() { int i, *arg, rv; pthread_t tids[NUM_PARTS]; initialize(x, 1); // initialize x[0..SIZE-1] to 1 for (i = 0; i < NUM_PARTS; i++) { arg = (int *) malloc(sizeof(*arg)); *arg = i*PART_LEN; pthread_create(tids+i, NULL, sumPart, (void *)arg); } for (i = 0; i < NUM_PARTS; i++) { pthread_join(tids[i], (void **) &rv); } printf(“sum: %d\n”, sum); }
P-threads:mutex locks pthread_mutex_t mutex_lock; pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t attr); pthread_mutex_lock(pthread_mutex_t *mutex); pthread_mutex_trylock(pthread_mutex_t *mutex); pthread_mutex_unlock(pthread_mutex_t *mutex); • pthread_mutex_init: initialize a mutex, use NULL for attrs. • pthread_mutex_lock: attempt to lock a mutex, will block if • the mutex is currently locked. • pthread_ mutex_trylock: attempt to lock a mutex, returns • with a busy error code if the lock is currently locked. • pthread_mutex_unlock: release a mutex. • returns an error (non-zero value) if the mutex • is not held by the calling thread
P-threads: race condition example pthread_mutex_t sumLock; int main() { int i, *arg; pthread_t tids[4]; void *retval; initialize(x); pthread_mutex_init(&sumLock, NULL); for (i = 0; i < NUM_PARTS; i++) { arg = (int *) malloc(sizeof(*arg)); *arg = i*PART_LEN; pthread_create(tids+i, NULL, sumPart, (void *)arg); } for (i = 0; i < NUM_PARTS; i++) { pthread_join(tids[i], (void **)&retval); } pthread_mutex_destroy(&sumLock); printf(“sum: %d\n”, sum); }
P-threads: race condition example void *sumPart(void *args) { int start = *((int) args); int end = start + PART_LEN; int i; int mySum = 0; for (i = start; i <end; i++) { mySum += x[i]; } mutex_lock(&sumLock); sum = sum + mySum; mutex_unlock(&sumLock); thread_exit(0); } Is there another way to solve this (particular) problem?
P-threads: race condition example void *sumPart(void *args) { int start = ((int) args); int end = start + PART_LEN; int i; int mySum = 0; for (i = start; i <end; i++) { mySum += x[i]; } // reuse arg space for return value *((int *) args) = mySum; thread_exit((void *)args); }
P-threads: race condition example int main() { int i,*arg; pthread_t tids[NUM_PARTS]; initialize(x, 1); // initialize x[0..SIZE-1] to 1 for (i = 0; i < NUM_PARTS; i++) { arg = (int *)malloc(sizeof(*arg)); *arg = i*PART_LEN; pthread_create(tids+i, NULL, sumPart, (void*)arg); } for (i = 0; i < NUM_PARTS; i++) { int *localSum; pthread_join(tids[i], (void **) &localSum); sum = sum + localSum; } printf(“sum: %d\n”, sum); }
Pthreads: stack sharing example void *addOne(void *arg) { int *x = (int *) arg; *x = *x + 1; return NULL; } int main() { pthread_t tid1, tid2; int y = 7; void *rv; pthread_create(&tid1, NULL, addOne, (void *) &y); pthread_create(&tid2, NULL, addOne, (void *) &y); pthread_join(&tid1, (void **) &rv); pthread_join(&tid2, (void **) &rv); printf(“y: %d\n”, y); }
Pthreads: functions with state void *useStatic(void *arg) { static int z = 0; z = z + 1; return (void *) &z; } int main() { pthread_t tid1, tid2; int *prv1, *prv2; pthread_create(&tid1, NULL, useStatic, NULL); pthread_create(&tid2, NULL, useStatic, NULL); pthread_join(tid1, (void **) &prv1); pthread_join(tid2, (void **) &prv2); printf(“rv1: %d\trv2: %d\n”, prv1, prv2); } • Real world example: gethostbyname
Pthreads: lock and copy pthread_mutex_t zlock; void *useStatic(void *arg) { static int z = 0; z = z + 1; return (void *) &z; } void *safeUseStatic(void *arg) { int *rv = (int *) malloc(sizeof(int)); int *tmp; pthread_mutex_lock(&zlock); tmp = useStatic(arg); *rv = *tmp; pthread_mutex_unlock(&zlock); return rv; } int main() { pthread_mutex_init(&zlock, NULL); // create threads to call safeUseStatic... }
Pthreads: functions with state int z; void initZ(int x) { z = x; } void *useGlobal(void *arg) { z = z + 1; return (void *)z; } int main() { pthread_t tid1, tid2; int rv1, rv2; initZ(7); /* create threads ... */ pthread_join(tid1, (void **) &rv1); pthread_join(tid2, (void **) &rv1); printf(“rv1: %d\trv2: %d\n”, rv1, rv2); } • Real world example: rand
Pthreads: state arguments void *useState(void *arg) { int z = *(int *)arg; z = z + 1; *arg = z; return (void *)z; } int main() { pthread_t tid1, tid2; int rv1, rv2; int z1 = 7, z2 = 7; // initialize the state pthread_create(&tid1, NULL, useGlobal, (void *)&z1); pthread_create(&tid1, NULL, useGlobal, (void *)&z2); pthread_join(&tid1, (void *) &rv1); pthread_join(&tid2, (void *) &rv2); printf(“rv1: %d\trv2: %d\n”, rv1, rv2); } • Real world example: rand_r
PThreads: Deadlock pthread_mutex_t A, B; void *lockAB(void *arg) { pthread_mutex_lock(&A); pthread_mutex_lock(&B); ....do work... pthread_mutex_unlock(&B); pthread_mutex_unlock(&A); } void *lockBA(void *arg) { pthread_mutex_lock(&B); pthread_mutex_lock(&A); ....do other work... pthread_mutex_unlock(&A); pthread_mutex_unlock(&B); }
Pthreads: Deadlock continued int main() { pthread_t tid1, tid; pthread_mutex_init(&A, NULL); pthread_mutex_init(&B, NULL); pthread_create(&tid1, NULL, lockAB, NULL); pthread_create(&tid2, NULL, lockBA, NULL); pthread_exit(0); } Solution: (1) define an ordering on the locks (2) acquire locks in order (3) release locks in reverse order
PThreads: Semaphores #include <semaphore.h> int sem_init(sem_t *sem, int pshared, unsigned int value); int sem_wait(sem_t *sem); int sem_post(sem_t *sem); int sem_destroy(sem_t *sem); sem_init: initialize a semaphore. • pshared should be zero. • value is the initial value for the semaphore sem_wait: wait for the value to be > 0, then decrement and then continue sem_post: increment semaphore (will wake up a waiting thread) sem_destroy: free the semaphore
Pthreads: Semaphore example struct job { struct job *next; // other fields related to the job } *queue; pthread_mutex_t q_lock; sem_t q_cnt; void initJQ() { pthread_mutex_init(&q_lock, NULL); sem_init(&q_cnt, 0, 0); queue = NULL; }
Pthreads: Semaphore example void process_jobs(void *arg) { while (1) { sem_wait(&q_cnt); pthread_mutex_lock(&q_lock); ... take job off queue ... pthread_mutex_unlock(&q_lock); ... process job ... } }
Pthreads: Semaphore example void enqueue_job() { pthread_mutex_lock(&queue); ... add job to the queue ... sem_post(&q_cnt); pthread_mutex_unlock(&queue); }
Condition variables: my turn example -- dumb int myTurn = 0; int numThreads = 4; pthread_mutex_t lock; void *myTurnFn(void *args) { int myID = *((int *)args); while (1) { pthread_mutex_lock(lock); /* get lock */ while((myTurn % numThreads) != myID) { pthread_mutex_unlock(lock); /* give it up! */ pthread_mutex_lock(t->lock); /* get it again */ } /* do some work */ pthread_mutex_unlock(lock); /* give it up! */ } }
Pthreads:condition variables A thread t: (1) grabs a lock (2a) tests to see if a condition holds, if so t proceeds (3a) signals when done. (2b) if not, goes to sleep, implicitly releasing the lock, (3b) re-awakes holding the lock when signaled by another thread. int pthread_cond_init(pthread_cond_t *cond, pthread_cond_attr_t *cond_attr); int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond);
Pthreads:my turn w/ CV int myTurn = 0; int numThreads = 4; pthread_mutex_t lock; // initialize to unlocked pthread_cond_t cv; // initialize to unavailable void *myTurnFn(void *args) { int myID = *((int *)args); while (1) { pthread_mutex_lock(&lock); while((myTurn % numThreads) != myID) { pthread_cond_wait(&cv, &lock); } /* do some work */ pthread_cond_broadcast(&cv); pthread_mutex_unlock(&lock);/*order matters! */ } }
typedef struct { pthread_mutex_t *lock; pthread_cond_t *cv; int *ndone; int nthreads; int id; } TStruct; void *barrier(void *arg) { TStruct *ts = (TStruct *)arg; int i; printf("Thread %d -- waiting for barrier\n", ts->id); pthread_mutex_lock(ts->lock); *ts->ndone = *ts->ndone + 1; if (*ts->ndone < ts->nthreads) pthread_cond_wait(ts->cv, ts->lock); else for (i = 1; i < NTHREADS; i++) pthread_cond_signal(ts->cv); pthread_mutex_unlock(ts->lock); printf("Thread %d -- after barrier\n", ts->id); }