|
|
HP-UX Process Management: White Paper > Chapter 1 Process ManagementBasic Threads Management |
|
The life cycle of a thread is analogous to that of a process. When you fork() a process, an initial thread is created. To take advantage of the kernel's ability to run flows of execution either concurrently or (on multiprocessor systems) in parallel, you will want to create additional threads. The following chart pairs the process-oriented function with its threads-oriented API function. In all cases, pthread functions are prefaced "pthread_"; in several cases, threads routines have no process equivalent. For complete specification, consult the manpages for pthread API functions in section three of the online reference. Table 1-10 Comparison of thread API and process functions
Pthread APIs consist of the following:
The following header files have definitions pertinent to threads:
Pthread API functions operate by the following rules:
Thread creation resembles process creation. The new thread starts execution at start_routine with arg as its sole parameter. The new thread's ID is returned to the creator of the thread. A thread has attributes that can be initialized prior to its creation. These include stack size, scheduling policy, and priority. For the newly created thread to have non-default attributes, you must pass a threads attribute object to pthread_create(). You can use the same threads attribute object to create multiple threads.
To create a new thread, the user or threads library must allocate the user stack. The kernel does not allocate stacks for threads, other than the initial thread. After a thread executes, it has two options:
In addition to the POSIX threads functions, HP-UX provides the extensions shown in the following table. These non-standardized interfaces are subject to change; efforts are being made to standardize them through X/Open. Table 1-11 Additional HP threads routines
POSIX provides four sets of synchronization primitives from which all other synchronization types can be built:
All synchronization is "advisory," meaning that the programmer has the ultimate responsibility for making it work. Synchronization objects can be process-local (used to synchronize threads within a process) or system visible (used by threads within different processes, such as by using shared memory or memory mapped files). Synchronization operations perform as follows:
The simplest mechanism for synchronizing threads is to use pthread_join(), which waits fora thread to terminate. However, this function is insufficient for multithreaded cases in which threads must synchronize access to shared resources and data structures. Mutual exclusion (mutex) locks allow threads to synchronize access to process resources and shared objects, such as global data. Threads wishing to access an object locked by a mutex will block until the thread holding the object releases it. mutex locks have attributes objects that can be set, based on the following characteristics:
When more than one synchronization variable is required by the same thread at the same time, lock order or hierarchy is vital to prevent deadlock. It is the programmer's responsibility to define the order of lock acquisition. Using a synchronization type called a condition variable, a thread can wait until or indicate that a predicate becomes true. A condition variable requires a mutex to protect the data associated with the predicate. A condition wait has two forms:
The condition wait operation releases the mutex, blocks waiting for the condition to signaled, at which time it reacquires the mutex. A condition signal operation has two forms:
Condition variables have only one attribute -- pshared. This indicates whether the condition variable is local to the calling process (the default, PTHREAD_PROCESS_PRIVATE) or shared by multiple processes. If shared, the caller must allocate the condition variable in shared memory. POSIX.1b named and unnamed semaphores have been "tuned" specifically for threads. The semaphore is initialized to a certain value and decremented. Threads may wait to acquire a semaphore.
These locks, a variant of the mutex lock, are useful when you have protected data that is read often but only occasionally written. Performance is slower than for a mutex. Read/write locks allow for any number of concurrent readers and no writers or a single write and no readers. Once a writer has access, readers are blocked from access until the writer releases the lock. If both readers and writers are waiting for a lock, the released lock is given to a writer. Read/write locks have two initializable attributes:
There are two types of signals:
Each thread has a signal mask used to block signals from being delivered to the thread. To examine or change the current thread's signal mask, use pthread_sigmask(), a function that behaves just like sigprocmask() in the process model. Do not use sigprocmask() to change the signal mask of a thread. Each process contains a signal vector that describes what to do for each signal (for example, ignore, default, or execute a handler). This signal vector is shared by all threads in the process. There may be only one signal handler for any given signal. This handler is used by all threads in the process. Each signal sent to a process is delivered once and only once to one thread within the process. Signals cannot be "broadcast" to all threads in a process. A POSIX function, sigwait(), allows a thread to wait for a signal to be delivered in a multi-threaded application. This is easier than installing signal handlers to handle the signal when it arrives and dealing with interrupted system calls. To wait for a signal, use int sigwait(sigset_t *set, int *signal); set is the set of signals being waited for, which must be blocked before calling sigwait. When a signal in set is delivered, this function returns and the signal being delivered is returned in signal. Threads may cancel or terminate other threads within their process. Threads targetted for cancellation may hold cancellation requests pending, similar to how signals are blocked and held pending. A thread's cancellation "state" determines whether cancellation is enabled or disabled (the latter blocks all requests). Valid states are PTHREAD_CANCEL_ENABLE and PTHREAD_CANCEL_DISABLE. A thread's cancellation type determines when a cancellation request is acted upon (that is, when the thread is terminated). A value of PTHREAD_CANCEL_DEFERRED (default) holds cancellation requests pending until the thread enters a function that is a cancellation point. If set to PTHREAD_CANCEL_ASYNCHRONOUS, the thread can be cancelled at any moment.
Thread cancellation cleanup handlers resemble signal handlers. However, a thread may have multiple handlers installed As a thread leaves a non-cancel-safe section, the cancellation cleanup handlers are removed. Any installed cancellation cleanup handlers are executed when
A function safe from cancellation is one that does not contain a cancellation point nor call a function that is a cancellation point.
Use the following thread cancellation cleanup handlers in your code:
|
|