Typically, the creation and termination of processes and threads
is directed by a pair of fork
and join
calls. The fork
call forks a new process
or thread off the active process or thread. The join
call waits for a termination of a process or thread. The exact syntax and
semantics depends on the particular operating system and programming
language.
To create a process, the Posix standard defines the fork
and execve
calls. The fork
call creates a child process, which copies much of the context
of the parent process and begins executing just after the fork
call with a return value of zero. The parent process continues
executing after the fork
call with the return value
providing a unique identification of the child process. The child process
typically continues by calling execve
to execute a
program different from that of the parent process.
To terminate a process, the Posix standard defines the exit
and wait
calls. The exit
call terminates a process. The wait
waits for
a child process to terminate and returns its termination code. Additional ways
for a process to terminate, both voluntarily or involuntarily, exist.
pid_t fork (void); int execve (const char *filename, char *const argv [], char *const envp []); pid_t wait (int *status); pid_t waitpid (pid_t pid, int *status, int options); void exit (int status);
The Posix standard call to create a thread is pthread_create
, which takes the address of the function executed by the thread as its main argument.
The pthread_join
call waits for a thread to terminate, a thread can terminate for example by returning from the thread function or by calling pthread_exit
.
The pthread_detach
call indicates that pthread_join
will not be called on the given thread.
int pthread_create ( pthread_t *thread, pthread_attr_t *attr, void * (*start_routine) (void *), void *arg); int pthread_join ( pthread_t thread, void **return_value); void pthread_exit ( void *return_value); int pthread_detach ( pthread_t thread);
The Posix standard also allows a thread to associate thread local data with a key and to retrieve thread local data of the current thread given the key.
int pthread_key_create ( pthread_key_t *key, void (* destructor) (void *)); int pthread_setspecific ( pthread_key_t key, const void *value); void *pthread_getspecific ( pthread_key_t key);
The Windows API provides the CreateProcess
call
to create a process, two of the main arguments of the call are the name of
the program file to execute and the command line to supply to the process.
The process can terminate by calling ExitProcess
,
the WaitForSingleObject
call can be used to
wait for the termination of a process.
Figure 2.10. Windows Process Creation System Calls
BOOL CreateProcess ( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ); VOID ExitProcess ( UINT uExitCode); DWORD WaitForSingleObject ( HANDLE hHandle, DWORD dwMilliseconds );
Windows applications can create threads using the CreateThread
call. Besides returning from the thread function, the thread can
also terminate by calling ExitThread
. The universal
WaitForSingleObject
call is used to wait for a
thread to terminate.
Figure 2.11. Windows Thread Creation System Calls
HANDLE CreateThread ( LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ); VOID ExitThread ( DWORD dwExitCode);
Windows also offers fibers as a lightweight variant to threads that is scheduled cooperatively rather than preemptively.
Fibers are created using the CreateFiber
call, scheduled using the SwitchToFiber
call, and terminated using the DeleteFiber
call.
Before using fibers, the current thread has to initialize the fiber state information using the ConvertThreadToFiber
call.
LPVOID ConvertThreadToFiber ( LPVOID lpParameter); LPVOID CreateFiber ( SIZE_T dwStackSize, LPFIBER_START_ROUTINE lpStartAddress, LPVOID lpParameter); VOID SwitchToFiber ( LPVOID lpFiber); VOID DeleteFiber ( LPVOID lpFiber);
Windows also allows a thread or a fiber to associate thread local data or fiber local data with a key and to retrieve the data of the current thread or fiber given the key.
DWORD TlsAlloc (void); BOOL TlsFree ( DWORD dwTlsIndex); BOOL TlsSetValue ( DWORD dwTlsIndex, LPVOID lpTlsValue); LPVOID TlsGetValue ( DWORD dwTlsIndex);
DWORD FlsAlloc ( PFLS_CALLBACK_FUNCTION lpCallback); BOOL FlsFree ( DWORD dwFlsIndex); BOOL FlsSetValue ( DWORD dwFlsIndex, PVOID lpFlsValue); PVOID FlsGetValue ( DWORD dwFlsIndex);
To permit graceful handling of stack overflow exceptions, it is also possible to set the amount of space available on the stack during the stack overflow exception handling.
BOOL SetThreadStackGuarantee ( PULONG StackSizeInBytes);
Linux offers an alternative process and thread creation API using the clone
call.
int clone ( int (*fn) (void *), void *child_stack, int flags, void *arg, ...);
The dynamic linker can be accessed through a standardized interface as well.
The dlopen
and dlclose
calls are used to load and drop a dynamic library into and from the current process.
Loading and dropping a library also involves calling its constructor and destructor functions.
The dlsym
call locates a symbol by name.
Special handles can be used to look up symbols in the default symbol lookup order or in an order that facilitates symbol wrapping.
void *dlopen ( const char *filename, int flag); int dlclose ( void *handle);
void *dlsym ( void *handle, const char *symbol);
Java wraps the operating system threads with a Thread
, whose run
method can be redefined to
implement the thread function. A thread begins executing when its start
method is called, the stop
method
can be used to terminate the thread.
class java.lang.Thread implements java.lang.Runnable { java.lang.Thread (); java.lang.Thread (java.lang.Runnable); void start (); void run (); void interrupt (); boolean isInterrupted (); void join () throws java.lang.InterruptedException; void setDaemon (boolean); boolean isDaemon (); static java.lang.Thread currentThread (); static void yield (); static void sleep (long) throws java.lang.InterruptedException; ... }
The traditional imperative interface to creating and terminating threads can be too cumbersome especially when trying to create applications that use both uniprocessor and multiprocessor platforms efficiently. The OpenMP standard proposes extensions to C that allow to create and terminate threads declaratively rather than imperatively.
The basic tool for creating threads is the parallel
directive, which states that the encapsulated block is to
be executed by multiple threads. The for
directive
similarly states that the encapsulated cycle is to be iterated by multiple
threads. The sections
directive finally states that
the encapsulated blocks are to be executed by individual threads. More
directives are available for declaring thread local data and other
features.
#pragma omp parallel private (iThreads, iMyThread) { iThreads = omp_get_num_threads (); iMyThread = omp_get_thread_num (); ... } #pragma omp parallel for for (i = 0 ; i < MAX ; i ++) a [i] = 0; #pragma omp parallel sections { #pragma omp section DoOneThing (); #pragma omp section DoAnotherThing (); }