5. Chapter 5: Distributed Operating System

5.5. Threads

  • Architectures for multi-threaded servers
    • The worker pool architecture
      • The server creates a fixed pool of ‘worker’ threads to process the requests when it starts up. 
      • The module marked ‘receipt and queuing’ is typically implemented by an ‘I/O’ thread, which receives requests from a collection of sockets or ports and places them on a shared request queue for retrieval by the workers. 
    • There is sometimes a requirement to treat the requests with varying priorities. 
      • Example: corporate web server could prioritize request processing according to the class of customer from which the request derives. 
      • Multiple queues for varying priorities in decreasing priority.
      • But, high level of switching between the I/O and worker thread as they manipulated the shared queue. 

        Figure : Client and server with threads

  • Thread-per-request architecture
    • I/O thread spawns a new worker thread for each request
    • Worker destroys itself when it has processed the request against its designated remote object.
    • The threads do not contend for a shared queue.
    • Throughput can be maximized because the I/O thread can creates as many workers as there are outstanding requests. 
    • But, overhead of the thread creation and destruction operations. 
  • Thread-per-connection architecture
    • Associates a thread with each connection
    • The server creates a new worker thread when a client makes a connection and destroys the thread when the client closes the connection. 
    • Client may make many requests over the connection, targeted at one or more remote objects.

       Figure : Alternative server threading architectures

  • Thread-per-object architecture
    • Associates a thread with each remote object. 
    • An I/O thread receive requests and queues them for the workers, but this time there is a per-object queue. 
  • For the last two architectures, the server benefits from lower thread-management overhead compared with the thread-per-request architecture. 
  • But, client maybe delayed while a worker thread has several outstanding requests but another thread has no work to perform. 
  • Threads within clients
    • Can be useful for clients as well as servers. 
    • Client process with two threads.
      • First thread: generates results to be passed to a server by remote method invocation, but does not require a reply. (Remote method invocation blocks the caller.)
      • Second thread: performs the remote method invocations and blocks while the first thread is able to continue computing further results. 
  • First thread places its results in buffers, which are emptied by the second thread. 
  • First thread is only blocked when all the buffers are full. 
  • Thread versus multiple processes
    • Threads are cheaper to create and manage than processes
    • Resource sharing can be achieved more efficiently between threads than between processes because threads share an execution environment. 
    • Switching to a different thread within the same process is cheaper than switching between threads belonging to different processes. 
    • Threads within a process may share data and other resources conveniently and efficiently compared to separate processes. 
    • But, threads within a process are not protected from one another. 

      Figure : State associated with execution environments and threads



  • Thread programming
    • Concurrent programming
    • Much threads programming is done in a conventional language, such as C with threads library. 
    • POSIX Threads standard IEEE 1003.1c-1995, known as pthreads, has been adopted recently. 
    • Some languages provide direct support for threads, including Ada95 [Burns and Wellings 1998], Modula-3 [Harbison 1992] and Java [Oaks and Wong 1999].
    • Java provides method for creating threads, destroying them and synchronizing them. 

      Figure : Java thread constructor and management methods

  • Thread synchronization
    • Main difficult issues: sharing of objects and the techniques used for thread coordination and cooperation.
    • Each thread’s local variables in methods are private to it. 
    • However, threads are not given private copies of static (class) variables or object instance variables. 
    • Race conditions might arise when threads manipulate data structures such as shared queues concurrently. 
    • Java provides the synchronized keyword for programmers to designate the monitor construct for thread coordination.
    • The monitor’s guarantee is that at most one thread can execute within it at any time.
       

      Figure : Java thread synchronization calls


  • Thread scheduling
    • Preemptive scheduling 
      • A thread maybe suspended at any point to make way for another thread, even when the preempted thread would otherwise continue running. 
    • Non-preemptive scheduling
      • A thread runs until it makes a call to the threading system, then the system may deschedule it and schedule another thread to run. 
      • Race condition can be avoided. 
      • But, cannot take the advantage of multiprocessor, since they run exclusively.
      • Care must be taken over long-running sections of code that do not contain calls to the threading system. 
      • Unsuited to real-time applications. 
  • Thread implementation
    • Many kernels provide native support for multi-threaded processes, including Windows, Linux, Solaris, Mach and Mac OS X. 
    • When no kernel support for multi-threaded processes is provided, a user-level thread implementation suffers from the following problems:
      • The threads within a process cannot take advantage of a multiprocessor. 
      • A thread that takes a page fault blocks the entire process and all threads within it. 
      • Threads within different processes cannot be scheduled according to a single scheme of relative prioritization. 
  • Thread implementation
    • User-level threads implementations, have significant advantages over kernel-level implementations:
      • Certain thread operations are significantly less costly.
      • Given that the thread-scheduling module is implemented outside the kernel, it can be customized or changed to suit particular application requirements. 
      • Many more user-level threads can be supported than could reasonably be provided by default by a kernel.