Basics Of Thread Programming – Part 3

Introduction to MFC Thread Programming

 
MFC wraps the windows API  for threading in a partial object oriented way. MFC has defined two kinds of threads based on the usage patterns. They are Worker threads and User-interface threads. Worker threads are useful for the jobs that will do work in the background  without any relation to events and user interactions. User-Interface thread are ideal for those kind of jobs that will relate with events, messages or user interaction for their processing.  

Worker Threads

Here are the most important features of MFC worker threads.
  1. Threads are to be created using one of the versions of AfxBeginThread.
  2. AfxBeginThread takes the pointer to function as one of the parameters. It  will start a new worker thread and makes the worker thread execute the function whose pointer is obtained as parameter.
  3. Other parameters to the function are the parameters to the function to be executed by the thread  and the operational properties of the thread like priority, stack size, state of thread after creation  etc. Here is the prototype of the method for worker thread.
CWinThread* AfxBeginThread( AFX_THREADPROC ptrToFunction, LPVOID parameterToFunction, int threadPriority = THREAD_PRIORITY_NORMAL, UINT stackSizeForThread = 0, DWORD threadCreationFlags = 0, LPSECURITY_ATTRIBUTES securityAttributes = NULL);
  1. Worker thread will terminate once the thread function is completely executed and control returns from it. So a simple return statement will terminate the execution of the thread.
  2. Other way of terminating the thread is by making the thread (to be terminated) to execute AfxEndThread() function.
  3. Also Worker threads can be terminated using Win32 functions but it may not be safe to do so according to the context.
  4. If  a thread has to be terminated based on some condition or based on the state of the system, then different kinds of techniques can be applied like polling to the value of some  variable(s) and based on the value of the variable, the course of thread execution can be changed.
Following is the screen shot of the sample application that creates  2 worker threads that update the progress bars from time to time.

“Start  Threads” button will create 2 threads with different methods to execute. Those methods will change the progress bar from time to time. The method will also poll to the value of a variable which when set to false, returns from the method , resulting in termination of the thread.  “Stop Threads” will set the variable to false forcing the method to terminate. “OK“ button will also do the same thing, but in addition it also terminates the application.

Here are the steps for making the sample application. These steps show only the threading part and not all the application. For complete application, refer to the complete code sample associated with this tutorial. It is assumed that the user has the knowledge of MFC and it’s basic programming like creating skeleton application, creating GUI, event handling etc. There are a lot of tutorials related to these basics in MSDN software and website.  

Creation Of MFC Application & GUI

  1. Create a dialog  based MFC application from the wizard.
  2. From resource editor add to the dialog, 2 progress bars and 3 buttons as shown in the above figure.
  3. Also create 2 control member variables to the dialog class for the progress bars and name them as firstProgressBar and secondProgressBar.
  4. Create the button click event handlers for the 3 buttons shown in the above figure.

Creation Of Thread Functionality

  1. Create two bool member variables to the dialog class runFirstThread and runSecondThread, which act as flags to indicate if the threads have to run or stop.
  2. Define the method for the first thread in the dialog’s .cpp file as following.
UINT showFirstProgress( LPVOID pParam ){
 int i=0;
CMTWorkerDlg* mtWorkerDlg = (CMTWorkerDlg *)pParam;
//if flag is set to false, then go to return statement to terminate the thread
while(mtWorkerDlg->runFirstThread == true) {
//let the progress value oscillate between 0 and 100
i = (++i)%101;
//update the status value to the progress bar
mtWorkerDlg->firstProgressBar.SetPos(i);
//introduce some delay so that the progress will change visually
::Sleep(30);
}
return(1);
}
This method will be executed by the first worker thread. It casts the void pointer to the dialog class. Then from time to it checks the value of the variable runFirstThread. If it is true then it increases the progress bar status and sleeps for some time, so that the progress will be incremented slowly and visibly. It takes care that the progress don’t overflow 100 which is the default maximum value of progress bar.  If runFirstThread is found to be false , then it’s an indication that the thread has to terminate and the method will be returned, making the thread executing this method to terminate.
  1. Similarly define the method for the second thread in the dialog’s .cpp file as following.
UINT showSecondProgress( LPVOID pParam ){
int i=0;
CMTWorkerDlg* mtWorkerDlg = (CMTWorkerDlg *)pParam;
//if flag is set to false, then go to return statement to terminate the thread
while(mtWorkerDlg->runSecondThread == true) {
//let the progress value oscillate between 0 and 100
i = (++i)%101;
//update the status value to the progress bar
mtWorkerDlg->secondProgressBar.SetPos(i);
//introduce some delay so that the progress will change visually
::Sleep(30);
}
//return the function to terminate the thread executing this method
return(1);
}
This works same as showFirstProgress() function but with the second progress bar and with a different flag runSecondThread.
  1. Add the following code to the button click event handler function of “Start Threads” button.
…………………………………………
//set the first thread's flag to run the thread
runFirstThread = true;
//set the second thread's flag to run the thread
runSecondThread = true;
//start the first thread with first progress bar and high priority
AfxBeginThread(showFirstProgress, this, THREAD_PRIORITY_ABOVE_NORMAL );
//start the second thread with second progress bar and low priority
AfxBeginThread(showSecondProgress, this, THREAD_PRIORITY_BELOW_NORMAL );
…………………………………………
This code will set the flags for the threads to run and continuously update the progress bars.  And it will start the threads with respective progress bars. “this” is to pass the pointer to the current dialog class to the thread methods which access the progress bars through it.. then the first thread is started with high priority and the second thread with low priority.
  1. Add the following code to the button click event handler function of “Stop Threads” button.
…………………………………………
//set the first thread's flag to stop the thread
runFirstThread = false;
//set the second thread's flag to stop the thread
runSecondThread = false;
//reset the progress of the first progress bar to zero  
firstProgressBar.SetPos(0);
//reset the progress of the second progress bar to zero  
secondProgressBar.SetPos(0);
…………………………………………
This code will set the flags for the threads to stop their execution immediately. Also it will set the status of the progress bars to zero.   
  1. Add the following code to the button click event handler function of “OK” button.
…………………………………………
//set the first thread's flag to stop the thread
runFirstThread = false;
//set the second thread's flag to stop the thread
runSecondThread = false;
//Let this thread wait for some time, so that those threads will terminate  in that time
::Sleep(40);
//Invoke default event handler to terminate the application
OnOK();
…………………………………………
This code will set the flags for the threads to stop their execution immediately. And  it will wait for some time, so that those threads will terminate in that time. Then it invokes the default event handler for “OK” button that will terminate the application.

 Refer to the complete working sample of the Worker threads accompanied with this article.

User-Interface Threads

Here are the most important features of MFC User-Interface threads.
  1. Threads objects are to be created using the subclass of CWinThread class.
  2. Threads are to be created and started  using CWinThread::CreateThread method and also by one of the versions of AfxBeginThread method.
  3. AfxBeginThread takes the pointer to the runtime class object of the CWinThread derived class. Other properties for the thread’s functionality like the priority, stack properties etc can be set with other parameters of the function.
  4. The object oriented way of creating the threads is using the CreateThread() method of CWinThread object. It creates the thread and makes the thread execute InitInstance() method of the CWinThread dericed class.
  5. So InitInstance() method should be overloaded with the functionality the thread has to execute. Once the control exits from this method, the thread terminates it’s execution.
  6. Other way of terminating the thread is by making the thread execute AfxEndThread() function.
  7. Also the threads can be terminated using Win32 functions but it may not be safe to do so depending on the context.
  8. The greatest advantage of using User-Interface threads over  worker threads is User-Interface threads can have message pumps. So the thread can be informed what it is expected to do at any point of time. So the thread can be manipulated in what ever the way it needs to be.
  9. If  a thread has to be terminated based on some condition or based on the state of the system, then a lot of techniques like event notification , message passage etc. can be used.
In the current article Event Notification technique is demonstrated. In the coming articles usage of message handling for inter thread communication  will be demonstrated.

Following is the screen shot of the sample application that creates  2 User-Interface threads that update the progress bars from time to time.

“Start  Threads” button will create 2 threads with 2  CWinThread derived class objects. These threads will change the progress of the progress bars from time to time. While updating the progress bar, they will check for the event that tells them to stop their execution.  “OK” button will set the event in it’s event handler  which will tell the thread to stop there, and then the application will terminate.  

Here are the steps for making the sample application. These steps show only the threading part and not all the application. For complete application, refer to the complete code sample associated with this tutorial. It is assumed that the user has the knowledge of MFC and it’s basic programming like creating skeleton application, creating GUI, event handling etc. There are a lot of tutorials related to these basics in MSDN software and website.

Creation Of MFC Application & GUI

  1. Create a dialog  based MFC application from the wizard.
  2. From resource editor add to the dialog, 2 progress bars and 4 buttons as shown in the above figure.
  3. Also create 2 control member variables to the dialog class for the progress bars and name them as firstProgressBar and secondProgressBar.
  4. Create the button click event handlers for the 4 buttons shown in the above figure.

Creation Of Thread Class

  1. Create a new class MyThread deriving it from CWinThread. This is the actual thread class which we create to update the progress bars from time to time. This class is planned so that each object of this class will keep take care of a single progress bar.
  2. Add the following variables to the class to keep track of the progress bar and it’s current progress value.
.......................................
//keep track of the current progress value of the progress bar managed by this thread
int count;
//pointer to the the progress bar managed by this thread
CProgressCtrl* pProgressBar;
//Handle to Event object to indicate that the thread has to stop it's execution
HANDLE threadEventKill;
.......................................
  1. Add the MFC macro “DECLARE_MESSAGE_MAP()” to the header file of the class.  To the .cpp file add the following code.
.......................................
IMPLEMENT_DYNAMIC(MyThread, CWinThread)
.......................................
BEGIN_MESSAGE_MAP(MyThread, CWinThread)
//{{AFX_MSG_MAP(CGDIThread)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()  
.......................................
          This fulfills the requirement of MFC thread class for instantiation and message pumping.
  1. Create or overload the constructer of the class to do the basic initialization like setting progress bar,  setting flags etc.
MyThread::MyThread(CProgressCtrl* pProgressBar) {
//This flag will destroy the thread object if the thread is terminated
m_bAutoDelete = TRUE;
this->pProgressBar = pProgressBar;
//Create the event object which will indicate the thread to stop it's execution
threadEventKill = CreateEvent(NULL, TRUE, FALSE, NULL);
}
m_bAutoDelete is a CWinThread member that destructs the thread object if the thread is terminated. With CreateEvent() method, the event is created and initialized to the false state because of it’s 3rd parameter. If it is set to true then it indicates that the event is raised to stop the thread.  
  1. In destructor destroy the event object.
MyThread::~MyThread() {
    CloseHandle(threadEventKill);
}
  1. Following are the CWinThread methods that are overloaded. They can be used for doing additional processing, but here they don’t do anything except delegating to the default fuctionality implemented by CWinThread
void MyThread::Delete() {
    CWinThread::Delete();
}

void MyThread::SuspendThread() {
    CWinThread::SuspendThread();
}

void MyThread::ResumeThread() {
    CWinThread::ResumeThread();
}
  1. Implement a method which increments the progress of the progress bar continuously. It takes care that the value wont overflow by oscillating the progress value between 0 and 100. Sleep will make the progress to change visibly. 
void MyThread::ShowStatus() {
    //Oscillate the value between 1 and 100
    count = (count+1)%101;
    //update progress bar
    this->pProgressBar->SetPos(count);
    //introduce some delay so that the progress changes become visible
    ::Sleep(100);
}
  1. Now implement InitInstance() method which determines the lifetime of the thread. Thread will live as long as it executes the code of this method or the code of the methods called from this method.
BOOL MyThread::InitInstance( ) {
    count = 0;
    //Is the event raised to stop/kill thread?
    while (WaitForSingleObject(threadEventKill, 0) == WAIT_TIMEOUT)
            //If not, then update the progress bar
            ShowStatus();
    // if raised, return from this method, resulting in termination of the thread
    return(FALSE);
 }
In a loop it checks if the event is raised for killing/stopping the thread using WaitForSingleObject() method. This method will check for the event. If the event is not raised, then the method will return the flag WAIT_TIMEOUT indicating the request has timed out. If it times out then it will update the progress by one step with the call to ShowStatus() method. As long as the event is not raised it will continuously update the progress bar. If the event is raised then the method will return, which cases the thread to terminate and this object to destroy.
  1. Now  create a method KillThread() which will implement the functionality to stop/kill the thread by raising an event. This method will raise the event with SetEvent() method. Before killing the thread, it will dereference the pointer to the progress bar. This method will be used by the GUI to tell to kill the thread.
void MyThread::KillThread() {    
    this->pProgressBar = NULL;
    //raise the event to stop/kill the thread
    VERIFY(SetEvent(threadEventKill));
}

Connect the GUI to the Threads    

  1. Create 2 pointers of the Thread class in the dialog class which will represent 2 threads.
    ................................
    MyThread* pMyThread1;
    MyThread* pMyThread2;
    ................................

  1. Instantiate the thread class with the pointers,  do this as part of the initialization.
    ................................
    pMyThread1 = new MyThread(this, &firstProgressBar);
    pMyThread2 = new MyThread(this, &secondProgressBar);
    ................................
  1. In the button click event handler of “Start” button , create the threads and let them run. After creation of threads set their priorities.
    ................................
    // create the threads and start them
    pMyThread1->CreateThread();
    pMyThread2->CreateThread();
    //after creation set their priority to high and low
    pMyThread1->SetThreadPriority(THREAD_PRIORITY_HIGHEST);
    pMyThread2->SetThreadPriority(THREAD_PRIORITY_LOWEST);
    ................................
  1. In the button click event handler of “Suspend” button , call the methods to suspend the threads.
    ................................
    pMyThread1->SuspendThread();
    pMyThread2->SuspendThread();
    ................................
  1. In the button click event handler of  “Resume” button , call the methods to resume the threads.
    ................................
    pMyThread1->ResumeThread();
    pMyThread2->ResumeThread();
    ................................
  1. In the button click event handler of  “OK” button, call the methods to kill the threads. Then call the default event handler for “OK” button which will terminate the application.
    ................................
    pMyThread1->KillThread();
    pMyThread2->KillThread();
    OnOK();
    ................................
Refer to the complete working sample of the User-Interface threads accompanied with this article.

MTWorker Project

The important files of this project are MTWorkerDlg.h and MTWorkerDlg.cpp which have the code for the dialog, GUI  and the event handlers. They also have the methods for the thread execution.

MTUserIntf Project

The important files of this project are MyThread.c , MyThread.cpp, MTUserIntfDlg.h and MTUserIntfDlg.cpp. MTUserIntfDlg.h and MTUserIntfDlg.cpp has the code for the dialog, GUI  and the event handlers. MyThread.c and  MyThread.cpp implements the thread class. Dialog class will control the threads over their lifetime.