(Latest Revision:
Sep 16, 2007
)
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.
- 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 on page 133 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 the first thread that is not
blocking it.
- The OS may provide a function to send a signal to one particular
thread.
- 4.5 Operating-System Examples
- 4.6 Summary