Thread
Programming
Written by Jose Mari Reyes
Copyright(C)1999, Jose Mari
Reyes
email:jet_reyes@jetemail.net
Manila, Philippines
No part of this document may be used or reproduced in any form
or by any means, or stored in
a database or retrieval system, without prior written permission of the author.
Making copies
of any part of this document for any purpose other than your own personal use
is prohibited
This document is presented as is, without warranty of any kind,
either express or implied,
respecting the contents of this document. The author disclaim all types of liabilities,
loss
or damage caused or alleged to be caused directly or indirectly by this document.
Introduction
Thread is
a very exciting technology, knowing and using this technology helps to make
application runs efficiently. This document will shows a minimal information
about threads, enough for the reader to create application with thread capabilities.
I am not an expert on this field. I know some of this technology which I think
enough for me to make a thread enable application, this lead me to write this
document to share knowledge to all. If you find misleading information or error
please email me, but be kind enough to not to say ill words. I spend some
of my spare time to provide readers this information so that everybody may benefit
from this technology. I disclaim all liabilities that may arise from the
use of any part of this material or document.
Platform
The examples
shown in this document was created using REDHAT LINUX 5.1 and Digital UNIX
OSF1 v3.2. I compiled the
code using the compiler that comes with both OS, the good and tested "cc" c
compiler. Our sample will make use of the POSIX threads. I cannot guarantee
that the code presented on this document will work on all UNIX platform, a modification
to the code might be needed.
The Thread, Thread against Process
Threads
are often called lightweight process. Compared with process, thread is small,
relatively cheap in terms of CPU cost. A process have its own address
space, file descriptor and other resources. A thread on the other hand
shares all those that belongs to a process, thus a thread is essentially a stack,
program counters and set of registers. There are two main kinds of threads,
these are User-space threads and Kernel-supported threads. User-space
threads operates without the support from the kernel, their state is maintained
within the user space, while the Kernel-supported threads gets it context switching
and scheduling from the kernel itself. User-space threads run as fast as the
Kernel-supported threads on a single processor machines but on multiple processor
machines the latter take advantages of this because the kernel schedule each
threads on a available processor, this let each thread to run in parallel. Context
switching between two threads in a single process is cheaper against the context
switching between two process.
Some Benefits of Threads
Thread Standards
The ISO/IEC
standard 9945-1:1996 also known as the ANSI/IEEE POSIX 1003.1-1995 includes
1003.1c this is the part that deals with thread. In this document we will
discuss portions of the POSIX thread standard and its implementation in the
DEC OSF1 thread.
Creating Threads
The call to the pthread_create function creates a new thread. Sypnosis:
#include <pthread.h>
int pthread_create(
pthread_t *thread ,
pthread_attr_t attr ,
pthread_startroutine_t start_routine ,
pthread_addr_t arg );
This function
takes 4 parameters, the first one, the *thread is the object of the thread created.
The second one is the thread object attribute, you can use pthread_attr_default
to specify default attribute. The third parameter is start_routine, this is
the function executed as the new thread. The last one is the address copied
and pass to the new thread. The newly created thread is by default joined
or undetach, to create detached thread see the function pthread_detach.
Detach Thread
By default, the thread created is non-detached meaning the calling thread is to wait for the termination of the called thread. Sometimes it is good to create a detached thread to reclaim storage. Call this function when a thread object is no longer referenced.
Sypnosis:
#include <pthread.h>
int pthread_detach( pthread_t
*thread );
Synchronizing Threads
POSIX threads provides two synchronization primitives, the mutex and condition variable. In our discussion we will use the first one. Mutexes are simple locking mechanism that can be used to control access to the shared resource. Remember that in thread everything is shared, if two threads will modifying the same resource at the same time, the data might get garbled. Good example is when two or more threads are running on same process, each thread is going to write to a variable which is our shared resource, can you imagine if the first thread writes 1 and the second writes 0 at a time on the same variable. Get the picture? This problem can be handled by syncronization by letting each thread to write and lock the shared resources releasing it only when done.
The minimal function to be called to do mutexes are:
int pthread_mutex_init(pthread_mutex_t
*mutex , pthread_mutexattr_t attr );
int pthread_mutex_lock(pthread_mutex_t
*mutex );
int pthread_mutex_unlock(pthread_mutex_t
*mutex );
The Application
All the necessary
function to do our sample application has been tackled above, now that we are
armed with sophisticated weaponry, we are now be able to create our first threaded
application. Our application will compute for the fibonacci of the set
of number recursively, but instead of computing the fibonacci per number, we
will do it simultaneously. Below is the source for such project.
/************************************************************
thread.c
Written by Jose Mari
Reyes
Copyright(c)1999, Jose
Mari Reyes
Demonstration program
using POSIX thread.
compiled to LINUX and
Digital Alpha v3.2
if to be use on LINUX
kindly define LINUX
***********************************************************/
#include <pthread.h>
#include <stdio.h>
#ifdef LINUX
#define pthread_attr_default
0
#endif
pthread_mutex_t mutex;
/* mutex object */
int
t_count; /* thread count */
struct Param
{
int NTIMES;
int NUMBER;
};
struct Param P[5];
unsigned fib(x)
/* compute fibonacci number recursively */
int x;
{
if (x > 2)
return (fib(x - 1) + fib(x - 2));
else
return (1);
}
void fibo (int N)
{
int i;
unsigned value;
printf("\nCreated
thread number:%d Iteration:%d Value:%d"
,N, P[N].NTIMES, P[N].NUMBER);
fflush(stdout);
t_count++; /*increment thread count*/
for (i = 1; i <= P[N].NTIMES; i++)
value = fib(P[N].NUMBER);
pthread_mutex_lock( &mutex);
printf("\nfibo(%d, %d) = result:%u",P[N].NTIMES, P[N].NUMBER, value);
fflush(stdout);
pthread_mutex_unlock( &mutex);
t_count--; /* decrement thread count*/
pthread_exit(NULL);
}
void main()
{
pthread_t th1;
int
N;
printf("\n\nPOSIX
Thread sample program written by Jose Mari Reyes");
printf("\nCopyright(C)1999,
Jose Mari Reyes\n\n");
pthread_mutex_init(&mutex, pthread_mutexattr_default);
for (N =
0; N < 5; N++)
{
P[N].NTIMES = 40 * N + 10; P[N].NUMBER = 20 + N;
pthread_create(&th1, pthread_attr_default, (void *) &fibo, (int *) N);
pthread_detach(&th1);
}
sleep(1);
while (t_count
> 0)
sleep(1);
printf("\nprocess end..\n\n");
}/*end of thread.c*/
Our source
will compute for the fibonacci of the sets of numbers, the number of repetition
and the value itself is stored in the array of param (see the structure param),
these sets is shared throughtout the application because it is declared global.
Since our mutex object will be used by our function, we declared it as public
mutex object. The initialization of our mutex object is accomplished by
the call to pthread_mutex_init, we dont have to modify its attribute.
The thread object is declared local to the main function since we are not going
to maintain many thread object. We create five(modifiable by adjusting
the value of for loop)threads by placing the pthread_create function inside
the for loop control structure, notice the last parameter, we passed the value
of N to the fibo function so that the fibo function will know what's the index
of its parameter. We detach every thread created so that we avoid unfreed
address when a thread dies or exit. We call the sleep system function
so that we can avoid a zero value on the t_count(we don't know which goes first)
and we can avoid our program to take all the CPU time. In the function
fibo we must synchonize the call to the printf function since this is not a
thread safe function, if we don't use any type of synchronization, the output
of printf might get garbled. The t_count is incremented each time a thread
is created and decremented each time the thread exit. The call to the
pthread_exit function terminates a thread.
Compiling Our Application
To compile
the source, we use the cc compiler. This compiler as far as my knowledge
goes is the standard compiler and included in any UNIX Operating System.
Below is our makefile.
(a modification to the compiler's
switch might be needed if the app will be compiled to other platform, in our
case we compiled it to Digital Alpha v3.2).
# MAKEFILE for thread.c
# Written by Jose Mari Reyes
# makefile for DCE Thread Digital
OSF
thread: thread.c
cc -D_REENTRANT -o thread
thread.c -lpthreads -lc_r
What do we covered
Our sample
application have covered some basic aspect of thread programming. We have
covered the method of creating thread, synchronization, inter-thread commmunication
(the structure param is shared), detaching a thread and exiting from thread.
Few Things To Try
The source
code presented above is somewhat crude, you can try modifying the code, for
example you can use semaphore instead of simple mutex lock synchronization method
or you can try new method to wait for a single thread to terminate.
Links to Source of Information(WEB)
Getting
Started With POSIX Threads by Tom Wagner and Don Towsley
Department
of Computer Science University of Massachusetts at Amherst
Linux
Threads Frequently Asked Questions (FAQ) by Sean Walton, KB7rfa
This page have been accessed
Redhat Linux is trademark of Redhat
Inc.
POSIX is an IEEE specification.
OSF1 is a trademark of Digital Equipment
Corp.