2. Thread Object
The PiThreads thread object provides
a user with the ability to generate a thread of execution which can easily me
managed through the public and protected member functions of the class
Thread. This is done by providing the
abstract base class thread. This class
is defined within the file thread.h, which must be included after #defining
POSIX or WIN.
The public and protected elements of
the thread class through which a user can interface with the thread object can
be seen below.
class Thread
public:
Thread();
virtual ~Thread();
bool
Create ();
bool
Create(unsigned long stack_size);
bool
CreateSuspended();
bool
CreateSuspended(unsigned long stack_size);
unsigned int
GetID() const;
bool Execute();
void
Resume();
void
Suspend();
void
Terminate();
bool
SetPriority(priority_level b_prio);
char *
GetPriority();
bool Join() const;
void
SafePoint();
void
Suspend_Thread();
void
T_Sleep(unsigned long millisecs);
protected:
virtual void ThreadRoutine() = 0;
In order to instantiate a thread, a
class is derived from the PiThreads class Thread, and provided the derived
class the default constructor Thread( ).
Then member function ThreadRoutine( ) is overridden with the
implementation specific thread routine.
A generic example of such a class can be seen below.
//#define WIN or POSIX
#include <threadcpp.h>
class ThreadDemo : public
Thread
{
public:
ThreadDemo() : Thread() {}
void EntryRoutine(void)
{
SafePoint(); //Allow
for thread suspension/termination
//Insert thread routine here
}
};
As declared above, the class
ThreadDemo instantiates a thread with an empty thread entry routine and an
empty thread exit routine. The member
function SafePoint() which is inserted in the thread routine will be discussed
shortly.
Now the derived class has been
defined, it can be instantiated.
PiThreads allows several methods to instantiate a thread. Create( ) instantiates a thread in running
mode such that it begins execution of its thread routine immediately upon
creation. CreateSuspended( ) initializes
a thread in a suspended state that does not begin execution of its thread
routine until a call to Execute( ) is issued.
Both of these functions have overloaded partners with a parameter
unsigned long stack_size. This
parameter allows for the user to instantiate a thread with an associated stack
size in bytes corresponding to the value passed to stack_size.
An example of all methods of instantiating a thread
can be seen below.
#define WIN or POSIX
void main()
{
ThreadDemo Threads[4];
Threads[0].Create();
//Creates a thread in running mode with default stack size
Threads[1].Create(1048576);
//Creates a thread in running mode with a stack
//size of 1MB
Threads[2].Construct(); //Creates a thread in suspended mode
with a
// default
stack size
Threads[3].Construct(1048576)//Creates
a thread in suspended mode with a
// stack size of
1MB
Threads[2].Execute();
//Begin execution of Threads[2]
Threads[3].Execute();
//Begin execution of Threads[3]
for( int I = 0; I < 4;
I++) //Wait for threads to complete
their thread routine by
Threads[I].Join(); //joining threads to main thread
}
A OSI_Thread thread object can also
have a priority level associated with it.
PiThreads defines seven levels of thread priority, and stored in a
variable of the defined type priority_level.
The relative priority levels which can be assigned to an PiThreads
thread object can be seen below in Table 2.
Type Name |
Constant |
priority_level |
IDLE |
|
LOWEST |
|
BELOW_NORMAL |
|
NORMAL |
|
ABOVE_NORMAL |
|
HIGHEST |
|
TIME_CRITICAL |
The desired priority level is then
passed to the function SetPriority(priority_level priority) . As with all the functionality of the thread
object, the member function can be called from either a reference to the class
instance a la dot operator or can be called from within the overridden
ThreadRoutine( ). An example of setting
a thread objects priority can be seen below.
#define WIN
void main()
{
ThreadDemo A; //Declare thread instance
ThreadDemo B; //Declare thread instance
A.Construct(); //Create A in a suspended state
B.Construct(); //Create B in a suspended state
A.SetPriority(HIGHEST); //Set A’s priority to HIGHEST
B.SetPriority(LOWEST); //Set B’s priority to LOWEST
A.Execute(); //Begin executing thread A
B.Execute(); //Begin executing thread B
A.Join(); //Wait for thread A to complete execution
B.Join(); //Wait for thread B to complete execution
}
After assigning a priority, the
thread instance can call GetPriority( ) to return the priority_level associated
with the thread, or GetNativePriority( ) to determine the numeric representation
of the threads priority on the respective platform.
As seen in all the examples thus
far, a thread can exercise join functionality with a call to Join( ). When the Join( ) member function is called
the calling thread is blocked until execution of the thread referenced by the
join has terminated. If execution of
the thread referenced by the join has already terminated, the calling thread is
not blocked. In all the examples thus
far, the instantiated thread is joined with the main thread at one point. When an instances Join( ) member function is
called from the main thread, the main thread is blocked until that instance of
a thread completes execution of its thread routine.
A thread object has the capability
of being able to suspend and resume execution of a thread with the member
functions Suspend() and Resume(). Also
along these lines, a thread can be forcible terminated with a call to
Terminate(). Suspend and terminate
functionality is offered with the assistance of the member function SafePoint(). SafePoint() is to be inserted at various
points throughout the thread routine if there are to be requests to suspend or
terminate thread execution as expressed through the Suspend() and Terminate()
member functions. SafePoint() must be
inserted in places in the thread routine where all side effects are accounted
for and thread preemption will cause no unspecified behavior. After a thread has been suspended, its
execution can be resumed by calling Resume().
An example of thread suspension and termination can be seen below
//#define WIN or POSIX
class ThreadDemo : public
Thread
{
public:
ThreadDemo() : Thread() {}
void EntryRoutine(void)
{
SafePoint(); //SafePoint
at start
//Insert thread routine here
for(…;…;…)
{
//thread routine
SafePoint();
//SafePoint() in loop
}
void ExitRoutine(void)
{
//Any clean-up can go here
}
};
void main()
{
ThreadDemo ThrA; //Declare thread object
ThreadDemo ThrB; //Declare thread object
ThrA.Create(); //Start executing thread routine
ThrB.Create(); //Start Executing thread routine
ThrA.Terminate(); //Forcibly terminate thread ThrA
ThrB.Suspend(); //Suspend execution of ThrB
ThrB.Resume(); //Resume execution of ThrB
ThrB.Join(); //Wait for ThrB to complete
execution
}
Here in addition to the start of the
thread routine, SafePoint() is inserted inside of a loop within the thread
routine at a position where all side effects are accounted for. Then in main(), two thread instances are
declared, ThrA and ThrB. ThrA and ThrB
are then initialized in a running mode with a call to Create(). ThrA is then forcibly terminated with a call
to its Terminate() member function.
Execution of ThrB. Is then suspended and then resumed with subsequent
calls to Suspend() and Resume().
Error handling in the PiThreads
thread object allows for graceful termination of a thread which has encountered
an error. The user can #define DEBUG,
and the thread object will output the source of any error which it
encounters. This functionality is
provided to assist debugging of a multithreaded aapplication developed with
PiThreads.