(Latest Revision: 
Sep 19, 2010
)  
Chapter Four -- Threads -- Lecture Notes
-  4.0 Objectives
     
     -  Understand the notion of a thread
     
 -  Discuss various API's for threads
     
 
 -  4.1 Overview
     
     -  What was the old-fashioned concept of a process?  The sequence of
	  executing instructions - the thread of execution - was one aspect of
	  a process.  However, a process also had a great many associated
	  resources: program counter, registers, run-time stack, primary
	  memory allocation, data section, text (code) section, open files,
	  allocated devices, and so forth.
          
     
 -  As the description above implies, processes were "heavyweight" and
	  as demand evolved for more and more processes in modern systems,
	  designers began looking for ways to be more economical in the manner
	  in which resources were used by resources.
     
 -  One early innovation was to organize processes so that more than one
          process could share the same program text.  There was also
	  experimentation with having processes share the same data section of
	  primary memory.
     
 -  These developments led designers to consider threads --
          "lightweight processes" which would share about as much process
	  context as possible.  
     
     
 -  Threads thus are a kind of process that help save on resource
          utilization.  If we have a need or desire to use, say 10, processes
	  to solve a problem, we don't necessarily have to replicate 10 copies
	  of each "process building block" - many of them may be
	  sharable by all 10 of the processes.
     
 -  Each thread needs the exclusive use of some resources: e.g.
	  ID, program counter, register set and runtime stack.
     
     
 -  Benefits of threads:
          
	  -   Responsiveness:  work is divided among threads and some
	       threads can continue working while others are blocked (e.g.
	       waiting for I/O to complete) [ Note this is a type of parallel
	       processing that applies to a uniprocessor ]
	  
 -   Resource Sharing:  e.g. sharing of code and data -
	       better utilization of primary memory.
	  
 -   Economy: It typically takes much less time to create a
	       thread compared with a heavy-weight process.
	  
 -   Utilization of Multiprocessor Architecture:  On a
	       multiprocessor, multiple threads can work together in parallel.
	  
 
     
      -  #1 and #4 are achievable using heavy-weight processes, but at a
          higher cost in terms of time and resources.
     
 -  It's a challenge to write mutlti-threaded programs.  There are the
	  questions of dividing the work, load balancing, division of data,
	  data dependency, testing, and debugging.
     
     
 
 -  4.2 Multithreading Models
     
     
     -  There are kernel level threads and user level threads.  Kernel
          level threads are supported directly by the kernel - each is
	  scheduled by the kernel.  Each is represented by a "thread control
	  block" Each kernel level thread can wait in the run queue, or a
	  device queue, an event queue, and so forth.  Each is treated as a
	  separate entity by the kernel.  Thus for example one kernel thread
	  can wait for I/O while another uses the CPU.
     
     
 -  User level threads are not represented individually at the kernel
          level. A package of library functions implements - you might say
	  'simulates' - the threads outside the kernel.  For example the
	  effect of the library might be to multiplex several threads using
	  just one kernel thread to support them.
     
     
 -  There has to be a correspondence between user and kernel level
          threads. The mapping can be many-to-one, one-to-one, or
	  many-to-many.
     
 
 -  4.3 Thread Libraries
     
     -  Posix thread (pthread) implementation varies from system to system -
	  could be user-level or kernel-level.
     
 -  Win32 threads are kernel-level 
     
 -  The Java thread API is typically implemented using a native thread
	  package on the host system (e.g. Pthreads or Win32).
     
 -  Students: study the example "Multithreaded C program using the
	  Pthreads API" carefully because the style of pthreads programming we
	  will do later is very similar.
     
     
 -  Section 4.3 contains three examples in this section of the test show
	  semantics for a parent thread to create a child thread to execute a
	  function.  The parent blocks until the child has exited, and then
	  the parent resumes execution.
     
     
 
 -  4.4 Threading Issues
     
     
     -  When an application is multi-threaded, should the fork() system call
          duplicate all threads, or just the caller?  Some API's provide both
	  options. 
     
     
 -  Implementations of exec() do not preserve the threads of the calling
	  process.  Therefore, if the child created by a fork() is going to
	  call exec(), typically there's no point in having the fork()
	  duplicate all the threads.
     
     
 -  Signals are a simple form of interprocess communication.  They
          behave something like interrupts but they are not interrupts.
     
 -  The OS delivers and handles signals.  These are routine 
          tasks it performs as opportunities arise.  For example, after
	  servicing an interrupt the OS might check to see if there is a need
	  to deliver or handle a signal.
     
 -  Multithreading complicates the problem of implementing signal
          delivery.  Should a signal be delivered to all the threads in a
	  process or just some?
          
     
 -  Often the handler for a signal should run only once.  A signal sent
	  to a process may be delivered only to the first thread that is not
	  blocking it.
     
 -  The OS may provide a function to send a signal to one particular
	  thread.
     
 -  Thread pools: Generally it is faster to use an existing thread to
          service a request, rather than create one and destroy it after it
	  performs the service.  Using a pool of threads also builds in a
	  limit on the number of threads a server can utilize - protecting the
	  system from too much thread proliferation.
     
 -  Typically threads have some need for thread-specific data.  This
          is data not shared with other threads.  In pthreads processing,
	  local variables can play this role.
     
 
 -  4.5 Operating-System Examples
     
     -  Linux employs a clone() system call with a flag parameter that
	  determines what resources will be shared between parent and child.
          
     
 
 -  4.6 Summary