Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Threads with MFC

0.00/5 (No votes)
1 Feb 2003 7  
Multithreading Programming with a MFC environment.

Sample Image - threads_and_mfc.gif

Introduction

This article is written to those who are beginners with the Multithreading programming and I have tried to illustrate some issues in this connection, and hope that it will be a good help for the beginners. It illustrates how threads can be created and shows how the amount of processor time for each thread varies due to the code in the threads' functions.

Threads

A thread is a basic unit to which the operating system allocates processor time and is the smallest piece of code that can be scheduled for execution. An application written for windows consists of one or more processes and a process is an instances of  an executing program. A thread runs in the address space of the process and can use the resources which are allocated to the process. A process always has at least on thread of execution and it's known as the primary thread. All threads in MFC applications are represented by CWinThread objects. The primary thread of MFC applications is implemented as a class derived from CWinApp, which itself is derived directly from CWinThread class.

It is important to consider that the threads do not run concurrently (I refer to systems with just one central processing unit), but it seems that they run at the same time, the reason is that the code execution switches quickly between threads. Whenever a thread uses its time slot, it should wait for its next turn to complete or continue its code execution. During the time the thread is not active, the code execution is going on in another thread, which becomes the current thread of the application. I will describe this with an example in the following:

Suppose you could create 5 threads in your application and each of these threads were capable of saying their thread's numbers, whenever they got their time slot. If so then it would be possible to hear the current thread's numbers coming out of the speaker.

The important fact in the example is that we can distinguish between different numbers coming out of the speaker, and if the threads ran at the same time we could not distinguish between the numbers, because then all the numbers should be heard at the same time and it  would not be possible to hear the numbers separately. At the end of this article I'll show you, how you can make such an application and hope it gives you a good understanding of the fact.

In contrast to Win32 API, MFC distinguishes between two types of threads:

1 - Worker Threads

These type of threads are used to complete background tasks when there is not a need for any user input. An example can be database backup functions. A worker thread can be created by implementing a function that performs the task your thread is to perform and passing the address of this function to the AfxBeginThread(...) function. The worker thread's function should have the following syntax:

UINT YourFunction(LPVOID pParam);

I will explain more about this in the demo application...

2 - User Interface Threads

These types of threads are able to handle user input and they implement a message map to respond to events and messages generated by user interaction with the application. To create a user interface thread, you must derive your own class from CWinThread class and pass run-time information about the class to the AfxBeginThread(...) function.

About the demo application:

As mentioned before for a worker thread, you define a function and decide the responsibility of your function in the thread. In the demo app. you can create 5 different worker threads with 2 responsibilities for each worker thread's function:

1- Drawing the corresponding thread's number 80 times into the applications main Window.

2-Showing a part of the code being executed, whenever the corresponding thread is running.

A variable loop is used in this function to force the operating system to obtain variable time slice for each thread's execution, you can change the loop's factor with the use of slider controls, which are associated with the threads and observe, how this change affects the speed of the corresponding thread. The threads' numbers has different colors when they are drawn to the applications main Window. When you create worker threads, you can see that the progress of the threads (code being executed in the loop) and the appearance of thread numbers in the applications main window, switches both among the running threads and threads numbers, indicating that the code execution switches from current thread to other threads, which have been waiting for their turn to use their time slice for completion of their code in their functions.

When the threads are running, the numbers from 1 to 5 with their corresponding colors appear on the screen, and it gives a good picture of the running threads in real time. The following image shows the worker threads on the fly.

The picture shows that the code execution has been switched from thread 1 to thread 2 ,and from thread 2 to thread 3 and from thread 3 to thread 4, and the screen shot is taken before the creation of thread 5. The interesting fact is that during the activation time all of the 5 threads including the primary thread of the application have been running (Multithreading concept), but at any point in the thread's activation time only one of these threads has been active and the others have been waiting for their turn to continue their code execution. The picture shows only the time during which the worker threads have been active, and does not show any picture of the other threads, which have been running in the system , but we have a prove for application's primary thread and that's this screen shot, and if the application's primary thread was not running It would not be possible to take this screen shot. The primary thread is active until the application's execution is stopped.

As the picture shows the competition between the running threads for ending their work (completion of their code) first, is not a fair one, as in the program, thread 1 is created first, and its obvious that thread 1 ends first and wins the competition. But the result of this competition is not always the same, and sometimes although thread 1 is created first and runs first, it can not win the competition , because it depends on the amount of work each thread is going to do. Sometimes like this case the work of the worker function in thread 1) is just to execute a short loop, and so it can finishes it's work before the program scope is switched to the other waiting threads, and the very first time slice for thread 1 has been long enough to complete it's work. But if the work is more (longer loop) thread 1 should also go to a waiting state, until it gets its turn back, and because the return of the program scope to the waiting threads is not always the same ,so there would be an equal chance for every thread to win the competition. The next picture shows that although thread 5 has been created and run last, it has won because the worker function (in thread 1) in this case has been more complicated than before and thread 1 has not been able to complete it's work during its first time slice, and it did go to a waiting state like the other threads, and so there has been a fair competition this time.

You may try this demo app. if you are beginner and view how the threads behave  . You can change the loop factor in the worker thread's function and view, how it affects the activation time of the threads. You can see that the more is the loop factor, the more is the worker thread function's responsibility (longer loop to execute ) and so the more time will be necessary, and so the activation time will increase. Using the "task manager" , keep eye on the CPU's Usage, and you can see ,that it increases rapidly during the activation time of the threads. Try to open another program for example MS word, when the threads are running, and you can see that it takes a longer time for MS word to open ,and the threads speed also decreases after opening MS Word.

Now let's start to make an application with User interface threads:

As I promised at the begining, I will guide you in making an application which has  User interface threads , and the code associated with these thread's are responsible for creating sound, saying their threads number, such that we are informed which of the threads are the current thread of the application. 

Step 1:

Using the AppWizard, create a dialog based application by choosing the "Dialog based" in the step 1 of the wizard and click finish.

Step 2:

As explained before, we have to drive our own class derived from CWinThread class and pass run-time information about the class to the user interface version of AfxBeginThread(...) function, so open the class view, write click on the classes (Yourprojectname class) and choose "New class" from the pop up menu.

Step 3:

From the "new class" dialog box ,create a new class with CWinThread as the "Base class" and use MFC class as the "Class type" and enter a name for this class.(CYourThreadClassName).

 

Step 4:

Save your project and look at the newly created class from the class view, inspect the source code for your thread class .Note that the wizard has provided stub implementation of InitInstance() and ExitInstance() functions and in these function you should add your class-specific implementation code. You may notice also that a message map is implemented for your thread class( as explained before the user interface threads implement a message map to respond to events and messages.)

Step 5:

If the thread class should respond to the user events it should be associated with an user interface object and we use an object of CDialog class. So create a dialog resource and associate it with a CDialog class. ( CYourDialogClassName)

Step 6:

Open the class view, expand your newly created dialog class and double click on the constructor function. Call the Create function in this function as below:

Create(YourDialogResourceID,NULL);

You find your dialog resource ID from the "Dialog Properties" by entering on the Dialog resource from the keyboard or by right clicking on the dialog resource  and choosing Properties from the pop op menu. 

Step 7:

Click on class view, expand the thread class and double click on the InitInstance() function. Now we create a dialog object in this function and assign the m_pMainWnd member variable of the thread class to this object.( By doing this ,our thread class  associates with a user interface object ). Add the following code into the IniInstance() function.

        m_pMainWnd=new CYourDialogClassName;
        m_pMainWnd->SetForegroundWindow();
        m_pMainWnd->ShowWindow(SW_SHOW);
        

Remember to include the header file of your dialog class to the currently opened file.

Step 8:

It will be a god idea to make an array of CWinThread pointers and assign these pointers to the created threads, so it would be easy to get our thread objects whenever necessary so open the class view, double click on the CYourProjectNameApp class and in the class definition add the following code:

CWinThread* m_pThread[5];

(Remember to add: extern CYourProjectNameApp theApp; at the bottom of the currently opened file, after DECLARE_MESSAGE_MAP()};)

Step 9:

Put a button in the main dialog window in order to create and run  the user interface threads.

Step 10:

With this new button selected, open the class wizard by entering CTRL+W from the keyboard, and make a command handler function.

Step 11:

Open this newly created handler function and add the following code in order to create 5 User interface threads:

        for(int i=0;i<5;i++)
             theApp.m_pThread[i]=AfxBeginThread(RUNTIME_CLASS(CYourThreadClassName));
        

(Remember to include the header file of your thread class (YourThreadClassName.h) at the top of the  currently opened file)

That's it, Save your project and build it .

By now ,we can create 5 user interface threads by clicking on the button in the main dialog window, and we can see that 5 dialog windows (you may move the upper dialog in order to see the other created dialog windows) have been created. Now we need to define threads responsibilities , and that is to hear the corresponding thread numbers ,talked from each thread, whenever the thread becomes active, so let's do that:

 

Step 12:

Open the resource view and double click on the Dialog resource ID of your newly created dialog ,in order to open the dialog editer.

from the main menu choose:

Project ->Add to Project->Components and Controls...

Step 13:

Now the "Components and Controls Gallery" should be opened, double click on the Registered ActiveX controls folder. Choose and insert the TextToSpeech class and close the "Components and Controls Gallery".

Step 14:

Now this new control should appear among other controls in the dialog editor.Drag this control and insert it into the dialog resource .Using class wizard add a member variable to this "textToSpeech" control.You may call it m_speaker .

Step 15:

Open class view, right click on the class which is associated with your newly created dialog resource( user interface windows) and choose "Add member Variable" from the pop up menu. Create a new variable with name m_ThreadNr and with the type CString.

Step 16:

Open the class view and expand the dialog class and double click on  the constructor function (YourDialogClassName()), add the following code in to this function

            // You have already written this line in step 6

        Create(YourDialogResourceID,NULL);
        int static nr=1;
        CString s;
        char buf[10];
        itoa(nr,buf,10);
        s="Thread #";
        s+=buf;
        s+= " Dialog Window";
        SetWindowText(s);
        switch(nr)
        {
                case 1:{m_ThreadNr="1";break;}
            case 2:{m_ThreadNr="2";break;} 
            case 3:{m_ThreadNr="3";break;}
            case 4:{m_ThreadNr="4";break;}
            case 5:{m_ThreadNr= "5";break;}
                        break;
         }
         // we want to be informed which thread is the current thread after 1 second

        SetTimer(nr,1000,NULL);      
                nr++;
        

Step 17:

Open the dialog resource (associated with the user interface threads) ,right click on the dialog resource and choose Events.

Choose WM_TIMER and click add and edit.

Step 18:

Add the following code in the OnTimer() handler function :

        CString str= "Thread";
        str+=m_ThreadNr;
        m_speaker.Speak(str);
           //you may delete this line if you want to hear the threads continuously

        KillTimer(nIDEvent);        
        

That was all, Save your work and build the project and try it. As you can see from the code after each second the current thread of the application says it's thread's number. Although there are more threads and details involved in this example, it illustrates that only one thread at a time is active.

As you may have seen in the demo app. when you increase the loop factor in a worker thread's function, that function uses more time to complete the code execution. You can use your project to  cause the same effect by increasing a particular thread function's responsibility (for example putting a loop to be executed ), by doing this you will hear that particular thread more often than the other threads and not just one time. Replace the code in the OnTimer() handler function by the following code and retry your project.

        CString str= "Thread";
        str+=m_ThreadNr;
        m_speaker.Speak(str);
        int i=atoi(m_ThreadNr);
        if(i==3)
         {
           for(int j=0;j<3;j++)
           m_speaker.Speak(str);
         }
        //you may delete this line if you want to hear the threads continuously

        KillTimer(nIDEvent);        
        

If you have a problem with stopping the threads voice after you have stopped the application , open the task manager and end the process with the name "vcmd.exe".

As I mentioned before, the user interface threads are able to handle user input and they implement a message map to respond to events and messages generated by user interaction with the application. So you can give more functionality to the threads by implementing message handler functions, as an example you can make a handler function ,which stops the threads to talk (by calling m_speaker.StopSpeaking(); in your handler function), whenever you click into the threads window and so on.

I hope that this article was useful for you.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here