Threading in VC++ by shours

Threading is an important part of writing applications.

Before you start writing the code, you must think twice on threading. First you have to identify the tasks that can be done by threads. Second you think or innovate a way to communicate between threads. Finally you start writing the code. For example an application receives some data on the serial port, computer the data and than plots some graphical means. For this application to run properly, you will have to create 3 threads:       – one for communication (working thread)       – one for computation (working thread)       – one for user interface (user thread) Now that you have created the 3 threads, it is time for you to start thinking of communication part. The first thread implements serial communication and it will transmit to the second thread (the computational one) the received and unpacked data. The second thread will do the computation and will send the results to the user-interface thread that will plot the data. Imagine that no thread had been created. The computation will be something like 10 exp 30. This will slow down or even kill your application. Or imagine that some problems may occur with the serial (e.g. waiting several seconds to receive some data that will not show up). This will also block your application. Now that I have presented you some reasons to use threads, we shall move on to “how to create a thread”.

How to create a thread

Right click the class view folder that contains your program and choose new class.

 

Choose the Base class to be CWinThread and name your class CThreadEx1.

 

  Hitting OK will bring you to the next configuration in your ClassView.

  The class have now the constructor, the destructor and two methods: InitInstance() and ExitInstance(). In the InitInstance do the initialization and in the ExitInstance do the cleanup. NOTE! The Constructor is protected! Move the constructor to the public zone, this way you can create thread objects in the OnCreate method of your application.

How to use a thread

You will add two more methods: OnIdle (use the ClassWizard to add this) an IsIdleMessage. The last one you will insert manually. In the header define virtual BOOL IsIdleMessage(MSG* pMsg); and the public overrides will look like this:

        public:      
               // Overrides
              // ClassWizard generated virtual function overrides
              //{{AFX_VIRTUAL(CThreadEx1)
              public:
              virtual BOOL InitInstance();
              virtual int ExitInstance();
              virtual BOOL OnIdle(LONG lCount);
              virtual BOOL IsIdleMessage(MSG* pMsg);
              //
            }}
             AFX_VIRTUAL

This will be the body of IsIdleMessage for the moment:

BOOL CThreadEx1::IsIdleMessage(MSG* pMsg)
{
     return CWinThread::IsIdleMessage(pMsg);
}

The communication to a thread it is done by messages. The thread receives messages in a queue and pops the messages one by one. The messages are then unpacked, understood, acknowledged and, of course, executed. When is ready to be processed, every message in the queue will be extracted and this event will occur in IsIdleMessage. Here you will have a pMsg pointer to a MSG structure that contains your message. When no messages are in the queue, OnIdle will occur. There are two type of messages that you could send to a thread: blocking and unblocking. The blocking messages type are posted using the SendThreadMessage(…). This means that when you use this function the execution of your program will stop at the SendThreadMessage() until the thread will execute the message. The unblocking messages type are posted using PostThreadMessage(…). The message will be posted to the queue and the execution of the program will continue asynchronous.

Example

To better understand threading I will demonstrate the functionality in a simple example. One simple dialog (a dialog-based project) will multiply some matrix. If you will multiply the matrix, let’s say when have clicked the Start button, the application will slow down until this job is finished. That’s the reason I implemented one thread to do the multiplying. Let me explain what this example does before we’ll get down to the code.

The functionality of the dialog:

When you will hit the start button one message will be posted to the thread.

pThreadEx1->PostThreadMessage(WM_THREADSTART, 0, 0);

Concomitantly a timer will be created that will fire at 0.1 seconds.

At this period will be posted recalculating messages:

pThreadEx1->PostThreadMessage(WM_THREADRECALCULATE, m_edit_dim, m_edit_pow);

If you hit Pause button the next message will be sent:

pThreadEx1->PostThreadMessage(WM_THREADPAUSE, 0, 0);

To stop the thread use Stop button:

pThreadEx1->PostThreadMessage(WM_THREADSTOP, 0, 0);

NOTE! Every post have three arguments one is the message definition (WM_THREADSTOP for example). This ones are defined in the header section of the thread class:

#define WM_THREADSTART (WM_USER + 101)
#define WM_THREADSTOP (WM_USER + 102)
#define WM_THREADPAUSE (WM_USER + 103)
#define WM_THREADRECALCULATE (WM_USER + 104)

The other two parameters are a WPARAM and a LPARAM that are in my case user defined. For example the WM_THREADSTOP message will not use WPARAM or LPARAM, that’s way I have left the spaces empties (or zero). For the recalculate message WM_THREADRECALCULATE I need to send the matrix dimension and one other parameter.

 

 When you close this dialog a WM_DESTROY message will be fired. The implementation of OnDestroy will contain few code lines that kill the thread and delete it so that no memory leaks will appear.

void CThreadTestDlg::OnDestroy()
{
    CDialog::OnDestroy();
    BOOL rez;
    DWORD exitcd;
    rez = GetExitCodeThread(pThreadEx1->m_hThread, &exitcd);
     if(rez && (exitcd == STILL_ACTIVE))
            rez = TerminateThread(pThreadEx1->m_hThread,0);
       delete pThreadEx1;
   }

The functionality of the thread:

No code adding to:

 CThreadEx1()
  ~CThreadEx1()
  InitInstance()
  ExitInstance()
  OnIdle(LONG lCount)

This is the IsIdleMessage method:

BOOL CThreadEx1::IsIdleMessage(MSG* pMsg)
{
     if(pMsg ->message==WM_THREADSTART)
     {
           start = TRUE;
           pause = FALSE;
           for(int i=0; i<500; i++)
             for(int j=0; j<500; j++)
               A[i][j] = 1;
     }
     if(pMsg ->message==WM_THREADSTOP)
     {
           start = FALSE;
           pause = FALSE;
     }          if(pMsg ->message==WM_THREADPAUSE)
     {
           if(start == TRUE)
             pause = !pause;
     }
     if(pMsg ->message==WM_THREADRECALCULATE)
     {
           if((start == TRUE) && (pause == FALSE))
           {
             int dim = pMsg->wParam;
             int pow = pMsg->lParam;
             int B[500][500];
             int sum;
             for(int i=0; i
             {
               for(int j=0; j 
               {
                     for(int k=0; k
                    {
                       sum = A[i][k]*A[k][j];
                     }
                     B[i][j] = A[i][j];
               }
             }
           return CWinThread::IsIdleMessage(pMsg);
         }
}

The thread will receive messages in the function above and will switch after the message type to do the appropriate work. I have used two variables start and stop that are thread-globally and point me if the thread is start/stop or if it’s paused.

Summary:

Thread:

– create class derived from CWinThread

– use the constructor, the destructor, InitInstance() and ExitInstance() to initialize and cleanup the thread.

– OnIdle(LONG lCount) occurs when no messages are in the queue

– receive messages in IsIdleMessage(MSG* pMsg) using                    if(pMsg->message==WM_THREADSTART) {…}

Application:

– create thread

o pThreadEx1 = new CThreadEx1();
o pThreadEx1->CreateThread();

– send messages to thread
o pThreadEx1->PostThreadMessage(WM_THREADSTART, 0, 0);

– delete thread
o BOOL rez;
o DWORD exitcd;
o rez = GetExitCodeThread(pThreadEx1->m_hThread, &exitcd);
o if(rez && (exitcd == STILL_ACTIVE))
o rez = TerminateThread(pThreadEx1->m_hThread,0);

o delete pThreadEx1;

Conclusion:

Even in this article I have covered just a minimal part of the thread technology, I have showed you that threading is necessary when you want to separate two businesses. You can easily create your own example starting from the summary.

By: Dragos BREZOI


Attachments

Source Files thread vc++

Project Files thread vc++