text: Unformatted text preview: ELEC 2602 Object Oriented & Systems Programming
  Module 4 Threads on Unix Systems 1 Threads : Overview
  Threads versus Processes (created by fork()); Basic properties of (POSIX) threads on Unix systems; Creating threads; Sample program showing the use of threads ! Managing threads; Thread-specific data; Sample codes of the readline() demonstrating the use of thread-specific data ! Using Threads vs. Processes in networked applications/services...
  2 Threads Versus Processes
  Traditionally, concurrency in network services is implemented by using fork;. Inter-Process Communication (IPC) is required to pass information between processes: Information from the parent to the child before the fork is easy, since the child starts with a copy of the parent's data space and with a copy of all the parent's descriptors. But returning information from the child to the parent takes more work. A thread is much lighter in weight--thread creation can be 10100 times faster than process creation. All threads within a process share the same global memory. This makes the sharing of information easy between the threads, but along with this simplicity comes the problem synchronization.
  3 Basic Properties of A (POSIX) Thread
  All threads within the same process share:
  process instructions most data open files (e.g., descriptors) signal handlers and signal dispositions current working directory, and user and group IDs. But each thread has its own:
  thread ID, set of registers, including program counter and stack pointer, stack (for local variables and return addresses), errno, signal mask, and priority. We focus on POSIX threads, i.e. a de facto standard on Unix, called Pthreads !
  4 Creating Pthreads
  #include <pthread.h> int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*func)(void *), void *arg);
  Each thread within a process is identified by a thread ID, whose datatype is pthread_t (unsigned int); When we create a thread, we specify a function for it to execute. The thread starts by calling this function and then terminates either explicity (by calling pthread_exit) or implicitly (by letting this function return). The address of the function is specified as the func argument, and this function is called with a single pointer argument, arg. If we need multiple arguments to the function, we must package them into a structure and then pass the address of this structure as the single argument to the start function.
  5 Managing Threads
  #include <pthread.h> int pthread_join(pthread_t tid, void **status); Similar to waitpid in process management; Must specify the tid of the thread that we wish to wait for; Waiting for "any" thread is quite complicated. [for interest, refer to Chp.11 of Steven's APUE] #include <pthread.h> pthread_t pthread_self(void); /* return the calling thread's own ID */ int pthread_detach(pthread_t tid); void pthread_exit(void *status); /* like a daemon, cannot wait */ /* exit */ 6 An Diagram of Echo Server Showing the uses of Threads..
  client stdin copyto thread pthread_create An Echo
  server stdout main thread Client-side Server-side 7 Sample Prog. # 1 pthread_create()...
  /* Adapted from A sample program named "threadid.c" downloaded from : The original source code is listed in "Fig. 11.2 - Printing thread IDs" of Steven's APUE NOTE: the program aims to illustrate the creation of a thread and then prints out the process & thread IDs of the new and main threads. */ #include <stdio.h> #include <unistd.h> #include <pthread.h> pthread_t ntid; 8 Sample Prog. #1 Cont'd..
  void printids(const char *s) { pid_t pid; pthread_t tid; pid = getpid(); tid = pthread_self(); printf("%s pid %u tid %u (0x%x)\n", s, (unsigned int)pid, (unsigned int)tid, (unsigned int)tid); } void * thr_fn(void *arg) { printids("\n new thread: "); return((void *)0); } 9 Sample Prog. #1 Cont'd..
  int main(void) { int err; err = pthread_create(&ntid, NULL, thr_fn, NULL); if (err != 0) perror("can't create thread: %s\n"); sleep(8); /* TRY to remove this line to see what happens ? */ printids("\n main thread:"); sleep(1); exit(0); } 10 Generated Output... 11 The Generated Output without the arbitrary delay...
  NOTE that : without the "sleep(8)" line, it will produce some non-deterministic output as below:
  $ ./threadid main thread: pid 3360new thread: tid 6684680 pid 3360 (0x660008 tid 6685016) (0x660158)
  12 Some Points to Note...
  After execution of the pthread_create() call, a new (POSIX) thread will start by executing the thr_fn() that calls the printids() to display the thread ID of the newly created thread. Thus, the thr_fn() provides the source code for the new thread to execute !! On successful return, the main() function also calls printids() to display the thread ID of the main thread. Note the use of "sleep(8)" to arbitrarily delay the call to printids() in the main() so as to allow the first call to printids() in thr_fn() to finish first !
  13 Thread-Specific Data (2) In POSIX.1, no less than 128 threadspecific structure per process flag in the Key structure indicates if the Key[0] structure is in use
  thread 0 Pthread{} other thread information pkey[0] pkey[1] pointer pointer NULL NULL Key[0] { { { flag destructor ptr flag destructor ptr Key[127]
  pkey[127] pointer NULL flag destructor ptr 14 Thread-Specific Data
  Thread-specific data structure is aimed to make a function thread-safe, i.e. function properly in concurrent execution, yet without the need to use static variables; Why we would like avoid the use of "many" static variables ? Recall that "static variables" are shared in the global data space for all threads. "Too many" static variables in such a global memory space is difficult to manage and may simply confuse the operations/logics of individual threads !!
  15 Functions for Thread-Specific Data
  #include <pthread.h> int pthread_once(pthread_once_t *onceptr, void (*init) (void)); int pthread_key_create(pthread_key_t *keyptr, void (*destructor)(void *value)); void *pthread_getspecific(pthread_key_t key); int pthread_setspecific(pthread_key_t key, const void *value); The 3 major steps involved in the use of thread-specific data:: To initialize the thread specific data : pthread_once() is called each time a function will use the thread-specific data; 2. To register a key, pthread_key_create() must be called exactly one time for a given key within a process; the key is returned through the keyptr pointer, and the destructor function, if non-null, will be called by each thread upon termination when the thread has stored a value for this key pthread_getspecific() and pthread_setspecific() functions are used to fetch and store the value associated with a key
  1. 16 Sample Codes of the readline() Using Thread-Specific Data
  Below is the sample codes of a thread-safe readline() ::
  #include "unpthread.h" /* predefined header file for this readline-threadsafe.c */ static pthread_key_t rl_key; static pthread_once_t rl_once = PTHREAD_ONCE_INIT; static void readline_destructor(void *ptr) /* the destructor function for clean-up */ { free(ptr); } static void readline_once(void) /* for step (2) key registration of the thread-safe readline() */ { pthread_key_create(&rl_key, readline_destructor); }
  17 Sample Codes of the readline() Using Thread-Specific Data...
  ssize_t readline(int fd, void *vptr, size_t maxlen) { Rline *tsd; /* tsd stands for thread-specific data (i.e. the actual value for rl_key) */ pthread_once(&rl_once, readline_once); /* step (1) initialization */ if ( (tsd = pthread_getspecific(rl_key)) == NULL) { /* test whether the key already has some value stored */ tsd = calloc(1, sizeof(Rline)); /* init to 0 by calloc() ref. to Mod-2 */ pthread_setspecific(rl_key, tsd); /* step (3) set the specific value for key rl_key */ } .... /* other detail ignored */ } 18 Using Threads Vs Processes for Networked Application...
  Concurrency Approach Single-threaded Advantages No context switching overhead; highly portable More portable for machines/OS without thread support Avoids creation cost Much faster than fork Avoids creation cost Disadvantages Does not scale for multiprocessor systems (common nowadays) Creation (spawning using fork()) cost high; resource intensive (e.g., memory) Requires mutual exclusion in some OS May require mutual exclusion; not as portable Requires mutual exclusion in some OS Process-per-request Process pool Thread-per-request Thread pool 19 ~~~~END of Module-4~~~~~ 20 ...
  View Full Document
  - Winter '08
  - DR.VINCENTTAM
  - POSIX, thread-specific data
  Click to edit the document details