Archives
 
 
 
  Special
 
 
 
  About Us
 
 
 

Newsletter
Free E-mail Newsletter from BYTE.com

 
    
           
Visit the home page Browse the four-year online archive Download platform-neutral CPU/FPU benchmarks Find information for advertisers, authors, vendors, subscribers Request free information on products written about or advertised in BYTE Submit a press release, or scan recent announcements Talk with BYTE's staff and readers about products and technologies

ArticlesWeaving a Thread


October 1995 / Core Technologies / Weaving a Thread

Solaris and Windows NT both support powerful multithreading/multiprocessing to help get the job done faster

Shashi Prasad

A symmetric multiprocessor (SMP), also called a shared-memory multiprocessor, has multiple CPUs sharing a common memory space. This memory sharing allows a single copy of the OS to be running on all the processors. SMP machines promise higher performance and better scalability, but the OS software must be designed to support multiprocessing and also be tuned for a scalable and efficient SMP. The OS, by means of some application programming interface (API), should allow the application developer to split his or her program into multiple pieces and run them simultaneously.

Loose Threads

A sequential proces s has a single flow of control, a sequence of instructions executed by the process. In a multithreaded process, there are multiple schedulable flows of control called threads. Threads are associated with a sequence of instructions and are often referred to as "threads of execution." Threads support the notion of concurrent programming and are used to exploit the inherent hardware parallelism of the SMP machine. By splitting a problem into smaller pieces, a thread can run each piece concurrently on an SMP machine.

Traditionally, applications were split into multiple processes, and some form of interprocess communications (IPC) was used to communicate between the processes. A multithreaded process has more than one thread of control sharing both address space and resources. Using threads eliminates the need for any IPC and reduces context-switching overhead. Threads are also referred to as lightweight processes (LWPs), since the context maintained for threads is much lighter than processes. Even though th reads are schedulable entities, it does not necessarily mean that they run in parallel. Threads can be scheduled concurrently on multiple processors, while on uniprocessor machines, threads are time-sliced.

With Solaris 2.2, Sun introduced a threads library for applications developers to take advantage of the multiprocessing and multithreading features of the advanced kernel. Solaris uses a two-tier thread library model. At the top layer are the user threads, and at the bottom layer are LWPs.

LWPs are kernel threads -- they use kernel resources and are the actual schedulable entities in Solaris. The kernel is aware only of LWPs and knows nothing about user threads. The user threads are managed by the threads library, which supports one-to-one, many-to-many, or many-to-one mapping between the user threads and the LWPs (see the figure "Thread Mapping in Solaris and NT" ). The multiplexing of user threads to a pool of LWPs and the scheduling of user threads is handled by the thread s library. Though the two-tier thread model adds some extra responsibility onto the user application, it allows for the cheap creation of user threads without using a lot of system resources.

Solaris has bound and unbound user threads. A bound thread is a one-to-one mapping between a user thread and an LWP. An unbound thread does not have a dedicated LWP; the threads library schedules the user thread on a pool of LWPs in a process. Applications that require global scheduling with respect to other LWPs in the system should use bound threads.

LWPs are scheduled by the kernel according to their scheduling class and priority. In Solaris, the process is created with an initial LWP, which inherits the scheduling class and priority of the parent process. Bound threads inherit the scheduling class and priority of the underlying LWP, while unbound threads inherit the scheduling class and priority of the process.

The Solaris kernel employs a preemptive priority-based scheduling system. LWPs of higher priority run before LWPs of lower priority. The Solaris threads library schedules user threads using priority levels on a pool of LWPs and chooses an LWP to execute user threads that are ready to run. If the LWP is blocked on an indefinite wait, the library saves the context of the blocked thread and assigns another thread to the LWP to run. The threads library usually creates enough LWPs to ensure that the process can proceed without an indefinite wait.

A Different Approach

Microsoft Windows NT was designed from the ground up to support multiprocessing and multithreading. Borrowing from the ideas of object-oriented design, Windows NT uses object classes to represent the system resources. In NT, both processes and threads are represented as objects. Threads are the schedulable entities in NT; every process in NT must have at least one thread before it can execute. Unlike the Solaris two-tier thread model , NT uses a straightforward one-to-one mapping between a user thread object and a kernel thread object.

Threads have 32 different priority levels and are broadly classified into two classes: real-time and variable. Real-time threads in NT are always scheduled ahead of other threads in the system, and the NT kernel does not alter the priority of real-time threads. The thread dispatcher uses a preemptive priority scheduler, with the highest priority thread always scheduled to run.

Threads in the variable class have a base and a dynamic priority. The base priority of a thread ranges two levels above and below the process base priority. The kernel periodically adjusts a thread's dynamic priority. For example, when a thread waits on an I/O, the kernel raises the dynamic priority of that thread. Threads that are CPU-bound tend to have lower dynamic priorities, while I/O-bound threads tend to have higher dynamic priority. A thread's dynamic priority can never fall below the thread's base priority.

Each process has a processor affinity, a set of processor s on which the threads of that process can run. Processor affinity affects thread scheduling. The NT kernel first picks the highest priority thread and then determines if the thread can run on the processor. If it can't, the next highest priority thread on the processor is chosen to execute. NT, like Solaris, employs soft processor affinity such that it always tries to schedule a thread on the processor on which it last ran.

Threads API

In addition to the SMP OSes, both systems provide an API for creating and using threads in an application. Solaris's threads API is based on the Unix International threads interface, and support for the Posix threads interface is planned in the upcoming release of Solaris 2.5. The Solaris threads interface is very similar to the Posix interface, and applications developed using the threads API on Solaris can be easily ported to use the Posix interface. Windows NT does not support the Posix interface, and applications use the Win32 interface to de velop multithreaded applications.

Any threads API can be broadly classified into such groups as thread management, thread synchronization, and thread-specific data. In both Solaris and NT, an initial thread is created when the process starts execution. Additional threads of control must be created by calling the thread-creation API. When a thread is created, it begins executing a start routine, and the new thread of control within the process is capable of independently being scheduled in both Solaris and Windows NT. Each newly created thread has its own stack, program counter, and a thread identifier. Thread identifiers in Windows NT are unique system-wide, while in Solaris they are guaranteed to be unique only within the context of the executing process.

Threads in a process exist in the same address space and share all the process resources, such as data variables, open file descriptors, and object handles. The sharing of data can be a blessing in disguise. Consider two threads in a multiproces sing environment concurrently updating a linked list. The results of such operations are undefined and can lead to data corruption. In a multithreaded program, any data that multiple threads can update simultaneously must be synchronized. Both Solaris and NT provide several thread-synchronization primitives.

Solaris and NT provide mutual exclusion, or mutex locks, to manage critical sections of the code. These mutex locks allow only one thread to execute the critical section of the code.

In multithreaded applications, it is common to divide the work between multiple threads. In such cases, one thread might have to wait for another thread to reach a particular state before proceeding. This form of synchronization is often called event synchronization. Solaris provides condition variables, while NT provides event objects for interthread synchronization.

In addition to mutexes and condition variables, Solaris provides semaphores and reader/writer locks for thread synchronization. Some of the ot her synchronization primitives in NT are critical section objects, semaphores, and I/O completion ports. The synchronization APIs on NT offer better functionality than those on Solaris and, except for critical section objects, provide the same APIs for process and thread synchronization. Though both OSes provide the basic functions for thread programming, the Posix threads interface provides much more advanced features than does the Win32 interface.

Developing multithreaded applications also requires an environment that supports multithreading. Solaris provides re-entrant versions for most of the commonly used libraries. Currently, Solaris does not provide thread-safe versions for Motif and OpenLook libraries, which are rarely used by multiple threads in a program. Windows NT also provides re-entrant versions for most of its commonly used libraries.

Debugging multithreaded applications is a big challenge and could be frustrating without the support of a thread-aware debugger. Solaris supports a mu ltithreaded debugger as a part of SPARCworks/iMPact, while Microsoft supports a multithreaded NT debugger as part of Visual C++. In addition to showing all threads of a process, both debuggers support suspending and resuming threads and inspecting variables on a per-thread basis.

The structured exception-handling (SEH) feature of Windows NT aids in the development of robust multithreaded applications. Terminating threads can always release held resources before exiting on exceptions, thus avoiding indefinite postponement. The exception-handling features of Windows NT are not language-specific.

Multithreaded Future

Solaris and Windows NT provide good environments for the development of multithreaded applications. They are by no means the only multithreaded OSes available at the desktop. NextStep, AIX (and other Unixes), and OS/2 provide thread support, as does Windows 95. Future versions of the Macintosh OS will also go threaded. The increasing availability of multiprocesso r hardware and the improved responsiveness realized even on uniprocessor machines means multithreaded applications are here to stay.


Thread Comparison

                                Solaris                 NT
                                ==============================
Thread model                    Two-tier                Direct
Posix thread                    No (planned             No
  API support                   for 2.5)
Re-entrant OS libraries         Yes (except for         Yes
  (i.e., thread-safe)           Motif/OpenLook)



Thread Mapping in Solaris and NT

illustration_link (6 Kbytes)

Solaris's various API-to-kernel thread mappings is flexible, but NT's direct mapping is simpler.


shaship@anstec.com or on BIX at editors@bix.com .

Up to the Core Technologies section contentsGo to previous article: Is There a GLINT in Your Future?Go to next article: The Standard Template LibrarySearchSend a comment on this articleSubscribe to BYTE or BYTE on CD-ROM  
Flexible C++
Matthew Wilson
My approach to software engineering is far more pragmatic than it is theoretical--and no language better exemplifies this than C++.

more...

BYTE Digest

BYTE Digest editors every month analyze and evaluate the best articles from Information Week, EE Times, Dr. Dobb's Journal, Network Computing, Sys Admin, and dozens of other CMP publications—bringing you critical news and information about wireless communication, computer security, software development, embedded systems, and more!

Find out more

BYTE.com Store

BYTE CD-ROM
NOW, on one CD-ROM, you can instantly access more than 8 years of BYTE.
 
The Best of BYTE Volume 1: Programming Languages
The Best of BYTE
Volume 1: Programming Languages
In this issue of Best of BYTE, we bring together some of the leading programming language designers and implementors...

Copyright © 2005 CMP Media LLC, Privacy Policy, Your California Privacy rights, Terms of Service
Site comments: webmaster@byte.com
SDMG Web Sites: BYTE.com, C/C++ Users Journal, Dr. Dobb's Journal, MSDN Magazine, New Architect, SD Expo, SD Magazine, Sys Admin, The Perl Journal, UnixReview.com, Windows Developer Network