Basics Of Thread Programming
Part 3
Introduction
to POSIX Thread Programming
POSIX is a standard that defines the standards for operating systems to
guarantee portability of code across different operating systems. As a part
of it, standards are defined for thread support, implementation etc. The
standard defines C language based API for programming threads with data structures,
functions etc. Most of the UNIX variants and Linux operating systems support
this POSIX threading standard. All these operating systems implement the
POSIX defined API for programming threads. Also the behavior of APIs and
threads will be same irrespective of these operating systems. Again all of
these operating systems may provide their own additional flavor of threading
libraries which may or may not be compliant with POSIX standard, examples
are Solaris Threads, Linux Threads etc. But in this tutorial where ever
"POSIX Threads" is used, it mean any threading library, which is compliant
with POSIX threading standard.
POSIX threads can be programmed simply in C or C++ language using any standard
C or C++ compiler. Other languages may have the wrappers for the standard
C language based threads or they may be implemented in proprietary way.
POSIX threads when programmed in C, will use structures, functions and some
other constants defined in pthread.h and sys/types.h header files. While
compiling and linking code that uses POSIX threads, options may have to be
defined to use pthread library which is the implementation library for POSIX
threads. In Cygwin(unix like environment on Windows) no such options are
required. In Linux "-d_REENTRANT" and "lpthread" options are required for
compiling and linking the code. It changes from operating system to operating
system. For such details please refer to operating systems documentation.
Each of the following section will list a code snippet of the program to
illustrate a particular feature of POSIX threads and at the last all the
programs are listed completely explaining them.
Here are the main features of POSIX threads.
- Thread programming APIs are provided in terms of functions which take
structures, pointers to structures, pointers to functions as their parameters.
- Thread is identified with a structure pthread_t which allows to identify
any thread uniquely and compare the identity of threads.
- Thread related functions generally return 0 for success and non-zero
integer for failure, but there are some functions that don't follow this convention.
- Operational characteristics of threads can be changed to configure
the functioning of threads using the attributes which are used while creating
the threads.
- To perform operations on the threads, instance of pthread_t will
be used to identify the thread.
- There is a default thread in every program that will start from main()
function and when it is returned from main() which results in process termination.
- Threads can be in one of the following states
- Created: Thread is just created and thread is not running,
but POSIX threads start running immediately after they are created.
- Running: Thread is in active state where its code is being
executed.
- Sleeping: Thread is in non active state where its code is
not being executed
- Stopped: Thread has completed the work it has to do, thread
will be destroyed, but its data can be retained optionally.
Following sections explain different concepts of thread life cycle management
and related APIs for programming the threads. While explaining the concept
only part of the program/code relevant to the context is shown. Please look
at the complete source code available on the web site for the forking samples
demonstrating the concepts explained here . There may be minor differences
between the partial code snippets and the complete code listing and it is
to make the illustration more effective.
Thread Creation/Starting
Creation of POSIX threads is to be done with pthread_create() function.
It will create the thread and start the thread immediately. Following is
the prototype of pthread_create() function.
int pthread_create(pthread_t *threadID, const pthread_attr_t
*threadAttributes, void*(*threadFunction)(void *), void *argumentToThreadFunction);
First parameter is a pointer to the pthread_t to return back the identity
or id of the new thread created. Using that information, further operations
will be done on the thread. Next parameter of type pthread_attr_t is to specify
the policy of the thread functioning like priority etc. To go with the default
policy pass null to it. Next parameter is a pointer to the function, which
will be executed by the thread over its lifetime. Thread function takes
void* as parameter whose value is provided as the next argument after the
function pointer. pthread_create() returns zero on success and non-zero value
on failure.
On execution of pthread_create() function a new thread will be created
and the new starts its execution immediately by default. The thread which
created the new thread will also continue its execution. But if the main
thread exits the main() function, of the process dies along with the newly
created thread is killed. See the following code snippet for details.
void *printNameOnThread(void *name){
/* print my name many times to the console */
}
int main() {
.
pthread_t threadID;
/* create my thread and return from main */
pthread_create (&threadID, NULL, printNameOnThread,
(void*)threadName);
/* process terminates here, so the new thread is
forced to terminate */
}
In the above code , in main() function the default thread creates a new
thread (which starts executing printNameOnThread() method), but it returns
from main. This causes the new thread to terminate. If the code is written
in the following way then the thread will get a chance to run for some more
time, because the main thread remains in main() for some more time.
int main() {
.
pthread_t threadID;
/* create my thread */
pthread_create (&threadID, NULL, printNameOnThread,
(void*)threadName);
/* do something here , the new thread will survive
and run as long as the current thread
doesnt return from main() */
}
Look for Create.c to find a complete working sample using this function.
Wait for the Thread to complete its execution
Newly created thread's execution will be stopped when the default thread
forces the termination of the process. But there is a way for any thread
to wait for completion of any other thread. This done by pthread_join() method.
This method will make the thread which invoked the function to wait for the
other thread to complete its execution. This is a blocking function where
the function will return only after the other thread terminates. Its prototype
is as following.
int pthread_join(thread_t tid, void **status);
First parameter is to identify the thread for termination of which, the
current thread will wait. Next parameter will return the status of the thread
after it completes. It can be null if not interested to retrieve the status.
As usual this returns zero on success and non-zero value on failure. Following
is the code snippet that uses this.
void *printNameOnThread(void *name){
/* print my name many times to the console */
}
int main() {
.
pthread_t threadID;
/* create my thread and return from main */
pthread_create (&threadID, NULL, printNameOnThread,
(void*)threadName);
//call join here so that the current/default thread
will wait till the thread identified by threadID completes its execution
pthread_join(threadID, NULL);
//following statements will be executed by current/default
thread after threadID terminates
.
}
Look for Join.c to find a complete working sample using this function.
Thread Identity, Current Thread and Comparing Threads
Thread is identified by the structure pthread_t defined in sys/types.h.
A pointer to this structure is passed to pthread_create() method to get the
identification information of the thread. This information is to be used where
ever the thread operations are done to inform on which thread the operations
are to be done. For example to wait for completion of a thread, pthread_t
instance corresponding to that thread should be provided as the parameter.
Following is an example.
int main() {
.
pthread_t threadID;
/* create my thread and set its information to threadID
variable */
pthread_create (&threadID, NULL, printNameOnThread,
(void*)threadName);
//to join the thread just created , use threadID
to tell to wait for that thread
pthread_join(threadID, NULL);
.
}
To get the identification information of the current thread POSIX has a
function pthread_self() which returns pthread_t instance with the information
of the thread (invoking the method) filled in it. In other words, pthread_self()
returns the pthread_t instance with the information of the thread that
invoked the pthread_self() function. Following code snippet illustrates
how to use the function.
Threads can be compared for equality using the pthread_equal() function.
It takes as parameters two pthread_t instances corresponding to the threads
which are to be compared. If they both correspond to the same thread then
it returns a non-zero value, and if they are representing different threads,
then it returns zero. Following is an illustration.
/* get the thread info of the current thread */
pthread_t threadID1 = pthread_self();
/* get again the thread info of the current thread into another
variable */
pthread_t threadID2 = pthread_self();
/* compare the info and they should be equal because threadID1
and threadID2 correspond to same thread */
if(pthread_equal(threadID1, threadID2))
printf("\nBoth the threads are equal");
..
Look for SelfEqual.c to find a complete working sample using these methods.
Initialization for the Thread
For initialization of Threads POSIX has defined some flags and functions
to do it safely. POSIX will allow to do the initialization only once if the
user wants it that way. Even by mistake if initialization is requested again,
initialization wont be done. pthread_once_t is a structure that keeps tracks
of the number of times the initialization function is invoked. If it is set
to the constant PTHREAD_ONCE_INIT then it is guaranteed that the first call
to pthread_once() function will execute and all the next calls will be discarded.
pthread_once() function will take the initialization function as one parameter
and the other parameter is pthread_once_t instance. pthread_once() function
for the first time will call initialization function which does the required
initialization. Following is an illustration.
.
/* initialize it to ensure that initialization function will be called only
once */
pthread_once_t once_status = PTHREAD_ONCE_INIT;
/* this call to do the initialization and initializeForThreading function
will be executed successfully */
pthread_once(&once_status, initializeForThreading);
..
/* calling it again, so initializeForThreading function wont be invoked
because it is already invoked once*/
pthread_once(&once_status, initializeForThreading);
..
Look for SelfEqual.c to find a complete working sample using them.
Sleep the Thread
Threads can be made to sleep with sleep() function of POSIX operating systems.
It is a more generic function that can be used anywhere even without POSIX
threads in context. Time interval for which the execution of the current thread
should be delayed can be given in seconds, minutes, hours and days. After
sleeping for the requested amount of time, threads will wake up automatically
to execute the next instruction after sleep() function call.
Wakeup the Thread from sleep
Thread will wake up automatically to execute the next instruction after
sleep() after the thread has slept for the requested amount of time. Nothing
needs to be done for waking up the thread unless it should be awaken before
the requested amount of time.
Termination of the Thread by itself
Thread will terminate its execution by itself in two ways apart from the
advanced techniques like signaling etc. One is normal termination where the
thread will complete the execution of the method provided for it at the creation.
Other way is the thread calling pthread_exit() function by itself so that
it gets killed before completion of its run. pthread_exit() is normally
used when the thread may decide to kill itself in response to some condition
satisfied at the runtime. Here is the code snippet for the thread termination
using pthread_exit() function.
// function that will be executed by the thread
after it is created
void *printNameOnThread(void *name){
int i;
char* threadName = (char*)name;
for(i=0; i<50; ++i) {
printf("\nPrinting my name : %s for %d time", threadName,
i);
/* pthread_exit() stops the execution of the thread
suddenly when the i takes a value of 25.
if the pthread_exit() function is not used in
the following block this for loop will execute 50 times
and thread will get terminated after that
normally.
*/
if(i==25) {
printf("\n* Requesting thread to terminate ", threadName);
pthread_exit(NULL);
/* this statement will never be executed because
thread dies after executing pthread_exit() statement */
printf("\n*Requested thread to terminate ", threadName);
}
}
/* this statement will never be executed because thread dies
after executing pthread_exit() statement */
printf("\n* Completed Execution On Thread : %s ", threadName);
}
result = pthread_create (pThreadID,
NULL, printNameOnThread, (void*)threadName);
Terminate of the Threads from other Thread
Threads can be killed from some other threads in different ways. Important
ways are canceling and killing the threads. In this section only cancellation
is explained. In the coming parts of the tutorial killing the threads with
signals will be explained. Threads when canceled, depending on their cancellation
status they may or may not terminate their execution. pthread_create() should
be used to cancel a thread from some other thread. pthread_t instance of
the thread to be terminated will be passed as a parameter to it. Also a thread
can set a cancellation point for itself where the thread will attempt to
terminate itself. This can be done using pthread_testcancel () function.
Thread's termination will depend on the cancellation state. It can be set
using pthread_setcancelstate() function. The parameters to it are new state
of the thread and a pointer to integer to which the old state will be set
after changing the cancellation state of the thread. The possible values
for cancellation state are the following
- PTHREAD_CANCEL_ENABLE : The thread will terminate when asked to cancel
it. This is the default value for threads.
- PTHREAD_CANCEL_DISABLE : The thread will not terminate when asked
to cancel it.
Cancellation type of the threads determine how and when the threads will
be terminated. It can be set using pthread_setcanceltype() function. The parameters
to it are new type of the thread and a pointer to integer to which the old
type will be set after the cancellation type is changed. The possible values
of the cancellation type are the following
- PTHREAD_CANCEL_DEFERRED : Thread will terminate at cancellation point
only. This is the default value for threads.
- PTHREAD_CANCEL_ASYNCHRONOUS : Thread will get cancelled where ever
it is. This is not recommended generally.
Here are the code snippets for illustrating the usage.
// function that will be executed by the thread
after it is created
void *printNameOnThread(void *name){
int i, oldState, oldType ;
if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldState) != 0) {
.}
.
if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState) != 0) {
.}
.
if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,&oldType) != 0) {
.}
.
if(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldType) != 0)
{
.}
.
pthread_testcancel();
}
..
int main(){
int i,j;
pthread_t threadID1, threadID2, threadID3, threadID4, threadID5;
/* create and start 5 threads whose cancellation policy is set
later in the method these threads execute */
runThread("T#1", &threadID1);
runThread("T#2", &threadID2);
runThread("T#3", &threadID3);
runThread("T#4", &threadID4);
runThread("T#5", &threadID5);
/* let those newly created threads run for a while */
sleep(5);
/* cancel 4 of the threads from the main/current/default thread
*/
cancelThread(threadID1);
cancelThread(threadID2);
cancelThread(threadID3);
cancelThread(threadID4);
/* let this thread wait till the last thread dies when it's
counter reaches 40 and when the cancepllation will be done by itself */
joinThread(threadID5);
}
SourceCode Description
Create.c
Create.c demonstrates the creation of threads and their termination due
to death of process. printNameOnThread() function is the one executed by
the threads after they are created. runThread() creates the new thread and
in turn associates the thread with the threads function with a pointer to
a function as a parameter. main() function creates the threads and enters
an empty for loop so that the newly created threads execute for some time
and when the for loop execution is complete, program terminates along with
the newly created threads.
Join.c
Join.c demonstrate the creation of new threads and making the main thread
to wait till the newly created threads complete their execution. printNameOnThread()
function is the one executed by the threads after they are created. runThread()
creates the new thread and in turn associates the thread with the threads
function with a pointer to a function as a parameter. waitForThreadToFinish()
function makes the thread invoking the function to wait for the thread represented
by threadID parameter to complete its execution. main() function creates
two threads and makes the main thread to wait for completion of the new threads.
Once.c
Once.c demonstrates the single time initialization for threads. initializeForThreading()
function will be invoked for doing the initialization required for threads.
But here it simply prints a message to the console to indicate that this function
is invoked and executed. main() method invokes the initialization function
twice and the function will be executed only once.
SelfEqual.c
SelfEqual.c demonstrates getting the identity of a thread and comparing
the threads for equality. main() method acquires the identity of a thread
twice and compares them. Since the information is corresponding to a single
thread they will be equal.
Exit.c
Exit.c demonstrates the termination of the thread by itself based on some
runtime condition. printNameOnThread() function is the one executed by the
threads after they are created. runThread() creates the new thread and in
turn associates the thread with the threads function with a pointer to a
function as a parameter. waitForThreadToFinish() function makes the thread
invoking the function to wait for the thread represented by threadID parameter
to complete its execution. main() function creates the thread and makes
the main thread to wait for completion of the new threads. But in printNameOnThread()
function, when the value of the variable i reaches 25, then the thread will
make a call to pthread_exit() function that terminates the thread.
Cancel.c
Cancel.c demonstrates the termination of the thread by other threads using
cancellation and also the termination of thread by itself using cancellation
points. printNameOnThread() function is the one executed by the threads after
they are created. This method according to the thread name sets different
cancellation policies for each thread and for the fifth thread it will set
a cancellation point to terminate itself. runThread() creates the new thread
and in turn associates the thread with the threads function with a pointer
to a function as a parameter. waitForThreadToFinish() function makes the
thread invoking it to wait for the thread represented by threadID parameter
to complete its execution. main() function creates 5 threads and sleeps
for some time for allowing the newly created threads to run for some time.
Then it cancels first 4 threads. After that it makes the main thread to
wait for completion of the new threads. But since the fifth thread has set
a cancellation point for itself it will terminate in the middle of it's execution.