Presentation is loading. Please wait.

Presentation is loading. Please wait.

Threads Programming Thread creation Synchronization.

Similar presentations


Presentation on theme: "Threads Programming Thread creation Synchronization."— Presentation transcript:

1 Threads Programming Thread creation Synchronization

2 Threads vs. Processes Creation of a new process using fork is expensive (time & memory). IPC is required to pass information between parent and child –A thread (sometimes called a lightweight process) does not require lots of memory or startup time. –It shares memory with parent, no IPC is required

3 fork() Process A Global Variables Code Stack Process B Global Variables Code Stack

4 pthread_create() Process A Thread 1 Global Variables Code Stack Process A Thread 2 Stack pthread_create()

5 Thread Programming Share heap, data, text segments, global variables, file descriptors, CWD, user, group ids, signal handlers, signal dispositions Individual stack, registers, program counters, errno, signal mask, priority

6 Introduction to Thread Programming Advantages of using threads  Increased efficiency  Shared memory (efficient communication) easier for monitor and control (e.g. statistics collection) Disadvantages of using threads  Synchronization overhead  Many library functions are not thread-safe (e.g., gethostbyname)

7 Thread Programming API Thread Management  creation, detach, join, exit  POSIX requires all threads are created as joinable threads Thread Synchronization  join  mutex (i.e. semapore with value of 1)  semaphore  condition variables

8 Client using threads

9 Client.c void *copyto (void *); static int sockfd; /* global for both threads to access */ static FILE *fp; void str_cli(FILE *fp_arg, int sockfd_arg) { char recvline[MAXLINE]; pthread_t tid; sockfd = sockfd_arg; /* copy arguments to externals */ fp = fp_arg; Pthread_create(&tid, NULL, copyto, NULL); while (Readline(sockfd, recvline, MAXLINE) > 0) Fputs(recvline, stdout); } void * copyto(void *arg) { char sendline[MAXLINE]; while (Fgets(sendline, MAXLINE, fp) ! = NULL) Writen(sockfd, sendline, strlen(sendline)); Shutdown(sockfd, SHUT_WR); /* EOF on stdin, send FIN */ return (NULL); /* return (i.e., thread terminates) when EOF on stdin */ }

10 int main(int argc, char **argv) { int listenfd, *iptr; thread_t tid; socklen_t addrlen, len; struct sockaddr *cliaddr; listenfd = Tcp_listen(argv[1], argv[2], &addrlen); cliaddr = Malloc(addrlen); for ( ; ; ) { len = addrlen; iptr = Malloc(sizeof(int)); *iptr = Accept(listenfd, cliaddr, &len); Pthread_create(&tid, NULL, &doit, iptr); } TCP Echo Server using Threads static void * doit(void *arg) { int connfd; connfd = *((int *) arg); free(arg); Pthread_detach(pthread_self()); str_echo(confd); /* same function as before */ Close(confd); /* done with connected socket */ return (NULL); }

11 Thread Creation initializes its attributes using the thread attributes object specified by the attr parameter thread attribues:  priority, stack size, name, detach-state, etc. start executing start_routine, with the parameter arg #include int pthread_create (pthread_t *thread, const pthread_attr_t *attr, void*(*start_routine) (void *), void *arg)

12 pthread_create() The return value is 0 for OK. Positive error number on error. Does not set errno !!! Thread ID is returned in tid

13 Thread Creation Each thread has a unique ID, a thread can find out it's ID by calling pthread_self(). Thread IDs are of type pthread_t which is usually an unsigned int. When debugging, it's often useful to do something like this: printf("Thread %u:\n",pthread_self()); When func() is called the value arg specified in the call to pthread_create() is passed as a parameter. func can have only 1 parameter, and it can't be larger than the size of a void *.

14 Thread Creation Complex parameters can be passed by creating a structure and passing the address of the structure. The structure can't be a local variable (of the function calling pthread_create)!!  threads have different stacks!

15 Thread Lifecycle – joinable thread similar to waitpid(), calling thread blocks Thread Termination 1.returns from its starting routing (main program) 2.call pthread_exit 3.cancelled by another thread pthread_cancel 4.process terminated status returned thread attributes specified

16 pthread_join Wait for a given thread to terminate by calling pthread_join.  similar to waitpid. #include int pthread_join (pthread_t tid, void ** status);

17 Thread Detach A thread can be changed to detached state A detached thread releases all its resource when it terminates A detached thread cannot be joined Example  pthread_detach(pthread_self()) #include int pthread_detach (pthread_t tid)

18 Thread-safe Functions Two of the functions are thread-safe only if the caller allocates space for the result and passes that pointer as the argument to the function.  inet_pton() //thread safe  gethostbyname() //not thread safe

19 Synchronization Problem #define NLOOP 5000 int counter; /* incremented by threads */ void *doit(void *); int main(int argc, char **argv) { pthread_t tidA, tidB; Pthread_create(&tidA, NULL, &doit, NULL); Pthread_create(&tidB, NULL, &doit, NULL); /* wait for both threads to terminate */ Pthread_join(tidA, NULL); Pthread_join(tidB, NULL); exit(0); } void * doit(void *vptr) { int i, val; /* * Each thread fetches, prints, and increments the counter NLOOP times. * The value of the counter should increase monotonically. */ for (i = 0; i < NLOOP; i++) { val = counter; printf("%d: %d\n", pthread_self(), val + 1); counter = val + 1; } return (NULL); }

20 Output

21 Mutex Variables Protects access to shared data resources A typical sequence  Create and initialize a mutex variable  Several threads attempt to lock the mutex  Only one succeeds and that thread owns the mutex  The owner thread performs some set of actions  The owner unlocks the mutex  Another thread acquires the mutex and repeats the process  Finally the mutex is destroyed

22 Mutex Variables #define NLOOP 5000 int counter; /* incremented by threads */ pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER; void *doit(void *); int main(int argc, char **argv) { pthread_t tidA, tidB; Pthread_create(&tidA, NULL, &doit, NULL); Pthread_create(&tidB, NULL, &doit, NULL); /* wait for both threads to terminate */ Pthread_join(tidA, NULL); Pthread_join(tidB, NULL); exit(0); } void * doit(void *vptr) { int i, val; /* * Each thread fetches, prints, and increments the counter NLOOP times. * The value of the counter should increase monotonically. */ for (i = 0; i < NLOOP; i++) { Pthread_mutex_lock(&counter_mutex); val = counter; printf("%d: %d\n", pthread_self(), val + 1); counter = val + 1; Pthread_mutex_unlock(&counter_mutex); } return (NULL); }

23 Lock / Unlock Mutexes pthread_mutex_lock() is used to acquire a lock on the mutex variable. If the mutex variable is already locked, then the thread will block. pthread_mutex_trylock() is used to acquire a lock on the mutex variable. If the mutex variable is locked, then the thread will NOT block. pthread_mutex_unlock() will unlock the mutex variable if called by the owning thread. #include int pthread_mutex_lock (pthread_mutex_t *mutex); int pthread_mutex_trylock (pthread_mutex_t *mutex); Int pthread_mutex_unlock (pthread_mutex_t *mutex);

24 Condition Variables pthreads support condition variables, which allow one thread to wait (sleep) for an event generated by any other thread. This allows us to avoid the busy waiting problem.  Mutex variables prevent simultaneous access to a shared variable  Condition variables allow threads to synchronize on the actual value of a shared variable Without condition variables, the programmer would need to have threads continually polling to check if the condition is met. A condition variable is always used in conjunction with a mutex lock

25 Busy Waiting Problem active_threads=0; // start up n threads on first n clients // make sure they are all running while (1) { // have to lock/release active_threads if (active_threads < n) //busy waiting // start up thread for next client }

26 Condition Variables (cont.) A condition variable is always used with mutex.  pthread_cond_wait(pthread_cond_t *cptr,  pthread_mutex_t *mptr);  pthread_cond_signal(pthread_cond_t *cptr); don’t let the word signal confuse you - this has nothing to do with Unix signals

27 Each thread decrements active_threads when terminating and calls pthread_cond_signal to wake up the main loop. The main thread increments active_threads when each thread is started and waits for changes by calling pthread_cond_wait. Condition Variables

28 All changes to active_threads must be inside the lock and release of a mutex. If two threads are ready to exit at (nearly) the same time – the second must wait until the main loop recognizes the first. We don’t lose any of the condition signals. Condition Variables

29 Global Variables // global variable the number of active // threads (clients) int active_threads=0; // mutex used to lock active_threads pthread_mutex_t at_mutex = PTHREAD_MUTEX_INITIALIZER; // condition var. used to signal changes pthread_cond_t at_cond = PTHREAD_COND_INITIALIZER;

30 Child Thread Code void *cld_func(void *arg) {... // handle the client... pthread_mutex_lock(&at_mutex); active_threads--; pthread_cond_signal(&at_cond); pthread_mutex_unlock(&at_mutex); return(); }

31 Main thread // no need to lock yet active_threads=0; while (1) { pthread_mutex_lock(&at_mutex); while (active_threads < n ) { active_threads++; pthread_create(…) } pthread_cond_wait( &at_cond, &at_mutex); pthread_mutex_unlock(&at_mutex); }

32 Typical Sequence of Condition Variables Main thread: Declare and initialize global data/variables which require synchronization (such as "count") Declare and initialize a condition variable object Declare and initialize an associated mutex Create threads A and B to do work

33 Thread A 1. Work up to the point where a certain condition must occur 2. Lock associated mutex mx and check value of a shared variable cv 3. Call pthread_cond_wait(cv,mx) to perform a blocking wait for signal from Thread-B. (Note: the blocking wait automatically and atomically unlocks the mutex mx ) 4. When signalled, wake up. Mutex mx is automatically and atomically locked. 5. Explicitly unlock mutex mx 6. Continue Thread B 1. Do work 2. Lock associated mutex mx Change the value of the shared variable cv that Thread-A is waiting upon. 3. Check value of the global Thread-A wait variable cv. If it fulfills the desired condition, signal Thread-A. 4. Unlock mutex mx. 5. Continue 1. Declare and initialize global data/variables which require synchronization (such as "count") 2. Declare and initialize a condition variable object 3. Declare and initialize an associated mutex 4. Create threads A and B to do work Typical Condition Variable Sequence MAIN THREAD

34 Waiting / Signaling on Condition Variables pthread_cond_wait() blocks the calling thread until the specified condition is satisfied. It should be called with mutex locked. pthread_cond_signal() is used to wake up another thread which is waiting on the condition. It should unlock mutex after calling pthread_cond_signal(). pthread_cond_broadcast() is used if there are more than one waiting threads. #include 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);


Download ppt "Threads Programming Thread creation Synchronization."

Similar presentations


Ads by Google