|
|
Title |
Using threads
|
Summary |
This note shows how to create and use threads for simple processing. It also touches on synchronisation. |
Contributor |
John McTainsh
|
Published |
1-Nov-2000 |
Last updated |
1-Nov-2000 |
|
|
Description.
Threads are an important and tricky part of a multi tasking operating system.
This article will show how to use threads and mutex but will not detail thread
design issues such as Deadlocks and Spinlocks.
How to start a thread.
It is necessary to create a function to run when the thread starts. This
will continuously run until the function returns. The programmer should
either use a flag or counter to limit the number of times the process loops.
//Function to display the thread count 20 times and terminate
UINT StartPlayingThread(LPVOID param)
{
AFX_MANAGE_STATE( AfxGetAppModuleState( ) ); //Thread safe MFC access.
TRACE( _T("StartPlayingThread(%p)\n"), param );
int *pnThreadNo = (int*)param;
ASSERT( pnThreadNo );
//Loop recording the count
for( int nCnt = 0; nCnt < 20; nCnt++ )
{
TRACE( _T("Count %d on thread %d\n"), nCnt, *pnThreadNo );
Sleep( 1 );
}
//Clean up
delete pnThreadNo;
return 0;
}
To launch the thread call AfxBeginThread with the starting function
name and one pointer. In this example the pointer is to an integer. This pointer
is a good way to share data with the thread. DO NOT pass
a pointer to a local variable as this may go out of scope and be deleted
before the thread that is using this memory terminates.
//Run thread one
int *pNo = new int;
if( pNo )
{
*pNo = 1;
AfxBeginThread( &StartPlayingThread, pNo );
}
//Run thread two
int *pNo2 = new int;
if( pNo2 )
{
*pNo2 = 2;
AfxBeginThread( &StartPlayingThread, pNo2 );
}
In the above example the two threads will run in parallel. Run
in debug and view the trace log to see the output.
Using Mutex.
As not all objects are "thread safe" (able to be processed by two threads
at the same time) it is often necessary to stop processing in a certain area
until another thread is finished in that area. In the example that follows
g_nCounter is incremented after an exact match test is
performed. If both threads increment g_nCounter at the same time
33 could be missed. EnterCriticalSection is used to hold other threads
out of the testing/processing area until the thread is finished
in this area.
//Shared Global counter
static int g_nCounter = 0;
//Function to loop until global g_nCounter == 33
UINT StartPlayingThread(LPVOID param)
{
AFX_MANAGE_STATE( AfxGetAppModuleState( ) ); //Thread safe MFC access.
TRACE( _T("StartPlayingThread(%p)\n"), param );
LPCRITICAL_SECTION lpCritSection = (LPCRITICAL_SECTION)param;
ASSERT( lpCritSection );
//Loop recording the count
bool bRun = true;
while( bRun )
{
EnterCriticalSection( lpCritSection ); //Only one thread in here
if( g_nCounter == 33 )
bRun = false;
else
g_nCounter++;
TRACE( _T("Thread ID=%ld, Count=%d\n"), AfxGetThread()->m_nThreadID, g_nCounter );
LeaveCriticalSection( lpCritSection );
Sleep( 1 );
}
return 0;
}
The following code creates a Critical Section handler to
be shared by two threads that increment a shared integer resource.
Once the threads are started they are monitored until they
exit or a 5 second timer elapses.
//Setup the mutex handler
CRITICAL_SECTION CriticalSection;
InitializeCriticalSection( &CriticalSection );
//Run thread one
CWinThread* wt1 = AfxBeginThread( &StartPlayingThread, &CriticalSection );
CWinThread* wt2 = AfxBeginThread( &StartPlayingThread, &CriticalSection );
//Wait for the threads to terminate in 5-10 seconds
if( WaitForSingleObject( wt1->m_hThread, 5000 ) == WAIT_TIMEOUT )
{
TRACE( _T("ERROR : Thread wt1. Shut Down Failed!!\n") );
exit( 0 );
}
if( WaitForSingleObject( wt2->m_hThread, 5000 ) == WAIT_TIMEOUT )
{
TRACE( _T("ERROR : Thread wt2. Shut Down Failed!!\n") );
exit( 0 );
}
//Clean up
TRACE( _T("Threads exited OK!\n") );
DeleteCriticalSection( &CriticalSection );
NOTE: It is important to note that it is not possible (to my knowledge)
to terminate a thread other than gracefully exiting thread function using software methods
or terminating the application. In the previous example if 5 seconds has passed
and the thread has not terminated it will NOT be forced to shut down.
For this reason it is important to plan how a the thread is going
to communicate and be terminated when designing the application. It is also
necessary to test the program on multi-processor systems which may process
threads differently that single processor machines.
|