Threads Chapter 26 Threads n n n Lightweight

  • Slides: 27
Download presentation
Threads Chapter 26

Threads Chapter 26

Threads n n n Light-weight processes Each process can have multiple threads of concurrent

Threads n n n Light-weight processes Each process can have multiple threads of concurrent control. What’s wrong with processes? n fork() is expensive n n n 10 to 100 times slower Copy of everything in parent. Inter process communication n For returning information from child to parent

Threads… n Shared components n n n Global memory Instructions Most data Open descriptors

Threads… n Shared components n n n Global memory Instructions Most data Open descriptors (files, sockets etc) Signal handlers Not shared n n n Registers, Program counter, stack pointer Thread ID Stack Errno Priority

Advantages of Threads n Light-weight n n n Lower Context Switching Overhead Fewer OS

Advantages of Threads n Light-weight n n n Lower Context Switching Overhead Fewer OS resources Shared State n Don’t need IPC-like mechanism to communicate between threads of same process

Disadvantages of Threads n Shared State! n n Many library functions are not thread-safe

Disadvantages of Threads n Shared State! n n Many library functions are not thread-safe n n Global variables are shared between threads. Accidental changes can be fatal. Library Functions that return pointers to static internal memory. E. g. gethostbyname() Lack of robustness n Crash in one thread will crash the entire process.

Creation n Thread equivalent of fork() n int pthread_create( pthread_t * thread, pthread_attr_t *

Creation n Thread equivalent of fork() n int pthread_create( pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg ); n Returns 0 if OK, and non-zero (> 0) if error.

Termination Thread Termination n n Return from initial function. void pthread_exit(void * status) Process

Termination Thread Termination n n Return from initial function. void pthread_exit(void * status) Process Termination n n exit() called by any thread main() returns

Waiting for child thread n int pthread_join( pthread_t tid, void **status) n Equivalent of

Waiting for child thread n int pthread_join( pthread_t tid, void **status) n Equivalent of waitpid()for processes

Detaching a thread n The detached thread can act as daemon thread n The

Detaching a thread n The detached thread can act as daemon thread n The parent thread doesn’t need to wait n int pthread_detach(pthread_t tid) n Detaching self : pthread_detach(pthread_self())

Echo client-server listenfd Server S 1 Client 1 Read Write Thread S 2 Read

Echo client-server listenfd Server S 1 Client 1 Read Write Thread S 2 Read Write Thread Client 2

Thread-based Echo Server

Thread-based Echo Server

main() { int listenfd, connfd; int len; pthread_t tid; /* Start the usual way

main() { int listenfd, connfd; int len; pthread_t tid; /* Start the usual way */ listenfd = Socket(…); Bind(listenfd, …); Listen(listenfd, …) for ( ; ; ) { len = addrlen; connfd = Accept(listenfd, …); /* Create a thread in service_func routine */ Pthread_create(&tid, NULL, service_func, ? ? ); } } How to pass connfd? (void *) arg

main() { int listenfd, connfd; int len; pthread_t tid; /* Start the usual way

main() { int listenfd, connfd; int len; pthread_t tid; /* Start the usual way */ listenfd = Socket(…); Bind(listenfd, …); Listen(listenfd, …) for ( ; ; ) { len = addrlen; cptr = Malloc(sizeof(int)); *cptr = Accept(listenfd, …); /* Create a thread in service_func routine */ Pthread_create(&tid, NULL, service_func, (void *) cptr); } }

void * service_func(void *arg) { int local_connfd; /* release parent from waiting */ Pthread_detach(pthread_self());

void * service_func(void *arg) { int local_connfd; /* release parent from waiting */ Pthread_detach(pthread_self()); /* extract connfd from argument */ local_connfd = *((int *) arg); free(arg); /* receive and echo client’s message See Figure 5. 3, page 124, Steven’s book*/ str_echo(local_connfd); /* Terminate the connection */ Close(local_connfd); return(NULL); }

Thread-based Echo Client Why is single threaded client not good enough? Multi-threaded Client

Thread-based Echo Client Why is single threaded client not good enough? Multi-threaded Client

int sockfd; FILE *fp; main() { pthread_t tid; fp = fopen(…); /* Start the

int sockfd; FILE *fp; main() { pthread_t tid; fp = fopen(…); /* Start the usual way */ sockfd = Socket(…); … Connect(…); /* Create a thread to send data */ Pthread_create(&tid, NULL, write_func, NULL); /* read data from sockfd */ read_func(); /* wait for child thread */ Pthread_join(tid, NULL); }

void * write_func(void *arg) { char sendline[MAXLINE]; while( more data in fp) Read from

void * write_func(void *arg) { char sendline[MAXLINE]; while( more data in fp) Read from fp into sendline[]; Write sendline[] into sockfd; Shutdown(sockfd, SHUT_WR); return(NULL); } void read_func() { char recvline[MAXLINE]; while ( more data from sockfd) Read from sockfd into recvline[]; Write from recvline[] to stdout; }

Locking in Threads

Locking in Threads

Mutex – for mutual exclusion int counter = 0; void *thread_func(void *arg) { int

Mutex – for mutual exclusion int counter = 0; void *thread_func(void *arg) { int val; /* unprotected code – why? */ val = counter; counter = val + 1; return NULL; }

Mutex… int counter = 0; ptread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void *thread_func(void *arg) { int

Mutex… int counter = 0; ptread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void *thread_func(void *arg) { int val; /* protected by mutex */ Pthread_mutex_lock( &mutex ); val = counter; counter = val + 1; Pthread_mutex_unlock( &mutex ); return NULL; }

Condition Variable – for signaling n Think of Producer – consumer problem n Producers

Condition Variable – for signaling n Think of Producer – consumer problem n Producers and consumers run in separate threads. n Producer produces data and consumer consumes data. n n Producer has to inform the consumer when data is available Consumer has to inform producer when buffer space is available

Without Condition Variables

Without Condition Variables

/* Globals */ int data_avail = 0; pthread_mutex_t data_mutex = PTHREAD_MUTEX_INITIALIZER; void *producer(void *)

/* Globals */ int data_avail = 0; pthread_mutex_t data_mutex = PTHREAD_MUTEX_INITIALIZER; void *producer(void *) { Pthread_mutex_lock(&data_mutex); Produce data Insert data into queue; data_avail=1; Pthread_mutex_unlock(&data_mutex); }

void *consumer(void *) { while( !data_avail ); /* do nothing – keep looping!!*/ Pthread_mutex_lock(&data_mutex);

void *consumer(void *) { while( !data_avail ); /* do nothing – keep looping!!*/ Pthread_mutex_lock(&data_mutex); Extract data from queue; if (queue is empty) data_avail = 0; Pthread_mutex_unlock(&data_mutex); consume_data(); }

With Condition Variables

With Condition Variables

int data_avail = 0; pthread_mutex_t data_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cont_t data_cond = PTHREAD_COND_INITIALIZER; void *producer(void

int data_avail = 0; pthread_mutex_t data_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cont_t data_cond = PTHREAD_COND_INITIALIZER; void *producer(void *) { Pthread_mutex_lock(&data_mutex); Produce data Insert data into queue; data_avail = 1; Pthread_cond_signal(&data_cond); Pthread_mutex_unlock(&data_mutex); }

void *consumer(void *) { Pthread_mutex_lock(&data_mutex); while( !data_avail ) { /* sleep on condition variable*/

void *consumer(void *) { Pthread_mutex_lock(&data_mutex); while( !data_avail ) { /* sleep on condition variable*/ Pthread_cond_wait(&data_cond, &data_mutex); } /* woken up */ Extract data from queue; if (queue is empty) data_avail = 0; Pthread_mutex_unlock(&data_mutex); consume_data(); }