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
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;
}
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);
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);
}
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.