Microsoft Visual C++ .NET Professional Projects - Premier Press
.pdf130 |
Part I |
|
INTRODUCING VC++.NET |
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
||||
|
|
The execution of the application begins with creation of an object of your appli- |
||||||||||
|
|
cation class. Following this, the control passes on to the InitInstance function of |
||||||||||
|
|
the application class. Here the document template is created and registered. If |
||||||||||
|
|
there are no command-line parameters, the ProcessShellCommand function |
||||||||||
|
|
invokes the OnFileNew function, which further invokes the OnNewDocument |
||||||||||
|
|
function of the document class. If any file name is specified on the command line, |
||||||||||
|
|
then the ProcessShellCommand function invokes the OnFileOpen function, which |
||||||||||
|
|
then invokes the OnOpenDocument function of the document class. The control |
||||||||||
|
|
then returns to the ProcessShellCommand, which then constructs the view and |
||||||||||
|
|
attaches it to the document. |
|
Y |
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|||
|
|
The application then enters into the message loop that is encapsulated in the Run |
||||||||||
|
|
member function of the CWinApp class.LThe message routing is handled by the |
||||||||||
|
|
OnCmdMsg member function of the CCmdTargetFclass, which is the base class for all |
||||||||||
|
|
objects that can handle messages. Figure 5-7 depicts the message routing as han- |
||||||||||
|
|
dled by the framework. |
|
M |
|
|
|
|
|
|||
|
|
|
|
|
|
A |
|
|
|
|
|
|
|
|
|
|
|
passedE |
|
passed |
|
|
|
||
|
|
|
|
|
|
|
|
|
||||
|
|
Application's |
|
on to |
|
Application |
|
on to |
Main frame |
|||
|
|
message queue |
|
T |
object |
|
|
|
window object |
|||
|
|
|
|
|
|
|
|
|
|
|
passed |
|
|
|
on to |
|
|
Is there any |
passed |
no |
|
on to |
|
|
message |
||
|
|
View class |
|
|
|
handler? |
|
passed |
|
|
If yes, then the view class |
on to |
|
|
|
|
|
handles the message. |
|
|
|
|
|
|
passed |
|
all unprocessed |
Document |
on to |
Frame |
messages, passed on to |
class |
|
window |
Application class
FIGURE 5-7 Message routing in a Document/View-based application
Team-Fly®
THE DOCUMENT/VIEW ARCHITECTURE |
Chapter 5 |
131 |
|
|
|
Summary
You’ve just gone through a fairly robust architecture offered by the MFC — the Document/View architecture. This architecture comes in handy for creating truly robust applications, which would otherwise be a tough and time-consuming task. It also gives you enough flexibility by allowing you to either stay with the given framework and create applications by tailoring it to the minimal, or edit and expand the framework to provide the required functionalities to the application. However, remember that not all applications have to be based on the Document/View architecture. Mostly, applications that have to deal with a lot of data and simultaneously interact with the user are designed using the Document/View architecture. It is the most apt choice because the architecture clearly demarcates data handling from data presentation and thus eases the task of handling both voluminous data and the presentation of data in various formats. If you are not designing such data-centric applications, you can choose to stick to applications that are not based on the Document/View architecture.
This page intentionally left blank
Chapter 6
Threads
In Chapter 5, you learned about the components of the Document/View architecture and also learned to create document-centric applications. In this chapter, you will learn about threads. This chapter discusses the basic concepts of threads and also
covers the procedure for creating single and multithreaded applications.
Basics of Threads
Before discussing threads, you need to learn about a related concept — processes. The operating system treats each executing instance of an application as a process, and each process has its independent system resources, such as memory and CPU timings. For instance, when you start Microsoft Word, Windows creates a corresponding process and allocates the required resources for the process. Also, operating systems, such as Windows 95 and higher versions of Windows, can support multiple processes running simultaneously. For example, you start Microsoft Word and Microsoft Excel. This results in creation of two processes.
Now you know what are processes. However, still you have a question unanswered. How are processes related to threads? Well, whenever a process is initiated, internally a thread is created that starts executing the application. In simple words, a thread is a path for execution within a process and can be defined as the smallest unit of executable code. Additionally, a thread by itself cannot request for any resources. As a result, it utilizes the resources that are allocated to the process of which the thread is a part.
As stated earlier, along with each instance of an application, a process is initiated, and each of these processes is in turn associated with at least a single thread. Thus, an application is always associated with a thread that is referred to as the primary thread. The primary thread consists of the startup code of an application and the address of the main or WinMain function of the application. When an application starts, the primary thread of the application is supplied to the operating system. If you terminate the primary thread of the application, the startup function, main or WinMain, also terminates and hence results in the termination of the related application.
You can create two types of threaded applications, single-threaded and multithreaded applications. These are discussed in detail next.
THREADS |
Chapter 6 |
|
135 |
|
|||
|
|
|
|
Single-Threaded Applications
An application that consists of only the primary thread is known as a singlethreaded application. In this type of application, a single thread handles the entire processing of the application, and hence all the tasks are handled in a sequence. Therefore, each task needs to wait until the previous task gets over.
Single-threaded applications are ideal for situations where you need to perform different tasks in a linear fashion, and do not require parallel processing for any task. Consider a situation in which you need to create an application that has to accept user input and store it in the hard disk. Here, you need to perform the two tasks sequentially. You need to accept data from the user, and then save the data on the hard disk. Obviously, you cannot store data before accepting it from the user. In such a situation, single-threaded applications come in handy.
Multithreaded Applications
Based on your experience, you know that very often your application has to handle multiple tasks simultaneously. So, how will your applications cater to such requirements? All you need to do is to create multithreaded applications. To understand the need for multithreaded applications better, consider a situation in which you need to create a gaming application for a car race. This application involves six cars that start from the starting point and compete until the finish line.
The first aspect to examine is how a single-threaded application works in this scenario. To begin with, out of the six cars, one starts from the start line and moves until it reaches the finish line. Next, the second car starts and reaches the finish line. This sequence continues until all six cars reach the finish line. The problem is pretty much obvious! Unless all six cars move together, you cannot categorize this as a race application. Such scenarios can be dealt with easily by designing multithreaded applications.
Any application that supports more that one thread is known as a multithreaded application. In a multithreaded application, you can handle multiple tasks simultaneously. Continuing with the example of the race application, you can create an individual thread for each car, and these threads can be executed parallel to each other. This means that the six cars in the race run independent of each other, and the application provides you the desired results.
Usually, you create additional threads to handle any background processing or other maintenance tasks. Some classic cases that can be implemented using
136 |
|
Part I |
|
INTRODUCING VC++.NET |
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
multiple threads are printing a document that is currently being edited by the user, |
|||||
|
|
|
|
and recalculating spreadsheet entries as soon as the user updates a cell. Thus, cre- |
|||||
|
|
|
|
|
|
ating multiple threads in an appli- |
|||
|
|
|
|
|
|
||||
|
|
|
MULTITASKING |
||||||
|
|
|
cation not only enhances its |
||||||
|
|
|
|
|
|
||||
|
|
|
|
Multitasking is the process of simultaneous- |
efficiency |
but |
also |
enables it to |
|
|
|
|
|
optimize its utilization of resources. |
|||||
|
|
|
|
ly executing more than one task. An appli- |
|||||
|
|
|
|
cation can handle pure multitasking |
Hitherto, |
we |
had |
categorized |
|
|
|
|
|
processes only if the application is running |
|||||
|
|
|
|
applications based on the number |
|||||
|
|
|
|
in a multiprocessor environment. In a sin- |
|||||
|
|
|
|
of threads they worked with. |
|||||
|
|
|
|
gle-processor environment, a Windows |
|||||
|
|
|
|
application simulates the multitasking |
Besides supporting these types of |
||||
|
|
|
|
process by using time slicing. Time slicing is |
applications, MFC |
also supports |
|||
|
|
|
|
the concept of dividing the CPU time |
two types of threads: user-interface |
||||
|
|
|
|
between different processes. Each process |
|||||
|
|
|
|
threads and worker threads. What |
|||||
|
|
|
|
is given a CPU time slice in a round-robin |
|||||
|
|
|
|
follows is |
a |
detailed discussion |
|||
|
|
|
|
fashion. In this method, while a process is |
|||||
|
|
|
|
about these types of threads. |
|||||
|
|
|
|
allocated the CPU’s time slice, other |
|||||
|
|
|
|
processes wait for their turn. Time slicing |
|
|
|
|
|
|
|
|
|
does not stop at the process level; it is |
User-Interface Threads |
||||
|
|
|
|
implemented at the thread level, too, and |
|||||
|
|
|
|
|
|
|
|
||
|
|
|
|
because the time slice for each thread is of |
The thread that handles the user |
||||
|
|
|
|
the order of milliseconds, multiple tasks of |
input is known as the user-interface |
||||
|
|
|
|
an application appear to be running parallel |
thread. In most cases, an applica- |
||||
|
|
|
|
to each other. However, in a multiprocessor |
|||||
|
|
|
|
tion has only |
one |
user-interface |
|||
|
|
|
|
environment, you can execute each thread |
|||||
|
|
|
|
thread. This thread handles all the |
|||||
|
|
|
|
on a separate processor and have all the |
|||||
|
|
|
|
threads running parallel to each other. |
events that are generated as a |
||||
|
|
|
|
Figure 6-1 displays the process of multitask- |
response to different user activities, |
||||
|
|
|
|
|
|
|
|
|
|
Application
Threads
Process A |
Process B |
FIGURE 6-1 The process of multitasking
THREADS |
Chapter 6 |
|
137 |
|
|||
|
|
|
|
such as a key press or a mouse click, and the windows messages. The MFC class that offers all functionalities of threads is the CWinThread class. Also, in the hierarchy of the MFC classes, the CWinThread class is the base class for the CWinApp class. Thereby, the CWinApp class inherits all thread-processing functionalities from its base class. As a result, one of the classic examples for a user-interface thread is the application object, which is an instance of the CWinApp or its derived class. From the preceding discussions, you can easily conclude that when you execute an MFC application, a user-interface thread is started.
Worker Threads
Another type of thread that you can create in an application is the worker thread. Worker threads do not handle user input, but perform background processing for an application. For example, when you create a spreadsheet application, in addition to the user-interface thread, you need to create worker threads to handle certain activities, such as recalculations, drawing graphs, and printing tasks, as background processes. Worker threads do not have message handlers and, therefore, cannot handle any event or window message. An application can have multiple worker threads to handle background processing; however, an application typically has only one user-interface thread.
You are now familiar with the basic concepts of threads, including the need for threads, the types of threaded applications that can be created, and the types of threads. With the basics in place, it’s time for you to learn about how threads are implemented.
More About Threads
Depending on the requirements, you can design either a single-threaded or multithreaded application. Further, in a multithreaded application, you can opt to include multiple user-interface or worker threads. One of the prerequisites for creating a thread in an application is to derive the application class from the CWinThread class so that your application will be capable of handling threads.
To create a thread, you use the AfxBeginThread function of the CWinThread class. The AfxBeginThread function has two overloaded versions: one for worker threads and another for user-interface threads. The next section discusses in detail these overloaded functions.
138 |
Part I |
INTRODUCING VC++.NET |
|
|
|
The AfxBeginThread Function for Worker Threads
The syntax for the AfxBeginThread function that you need to use to create a worker thread is as follows:
CWinThread* AfxBeginThread(AFX_THREADPROC ThreadFunction, LPVOID Parameter, int ThreadPriority
=THREAD_PRIORITY_NORMAL, UINT
StackSize = 0, DWORD
ControlFlags = 0,
LPSECURITY_ATTRIBUTES Security = NULL);
Following is a description of the parameters of the AfxBeginThread function:
ThreadFunction. Is substituted by the name of the function that you need to execute with the thread. When you run the thread, it provides the address of this function to the processor. This parameter cannot be NULL because a thread needs at least one function to execute. ThreadFunction must be a global function.
Parameter. Is substituted by any parameters that you need to provide to
the function that you execute with the thread.
ThreadPriority. Specifies the priority that you want to set for the thread. The predefined integer values that you can provide to set the thread priority are displayed in Table 6-1. The threads with the highest priority are executed first.
Table 6-1 Thread Priority Values1
Value |
Description |
THREAD_PRIORITY_HIGHEST |
Specifies the highest priority value for a |
|
thread |
THREAD_PRIORITY_NORMAL |
Specifies the normal priority value for a |
|
thread |
THREAD_PRIORITY_ABOVE_NORMAL |
Specifies one point above the normal |
|
priority value |
THREAD_PRIORITY_BELOW_NORMAL |
Specifies one point below the normal |
|
priority value |
THREAD_PRIORITY_LOWEST |
Specifies the least priority value for a |
|
thread |
1All the values for the thread priority are predefined integer values.
THREADS |
Chapter 6 |
|
139 |
|
|||
|
|
|
|
StackSize. Specifies the amount of stack memory (in bytes) that you want to allocate to the thread. When you specify 0 for the StackSize, the default stack size is set equal to the stack size of the thread that creates the current thread.
ControlFlags. Specifies the additional flags that you can specify for the
thread creation. For ControlFlags, you can either specify 0 or CREATE_SUSPENDED values. When you specify 0, the thread starts immediately after it is created, whereas when you specify CREATE_SUSPENDED, the thread is created in a suspended mode. To execute the thread, you need to call the CWinThread::ResumeThread function. You create a suspended thread if you want to initialize any of the member data of the
CWinThread class, such as m_bAutoDelete.
NOTE
m_bAutoDelete is the public data member of the CWinThread class and is of BOOL type. The TRUE value for this data member specifies that the thread is deleted automatically when the thread function terminates.
Security. Specifies the security attributes for a thread and points to the SECURITY_ATTRIBUTES structure. This structure contains the security descriptor for an object. If you specify NULL for the Security parameter, the security attributes of the thread that is creating the current thread are set as the default security attributes.
The AfxBeginThread function returns a pointer of CWinThread type that can be used to control the thread. For example, the pointer enables you to execute the
SuspendThread and ResumeThread functions of the CWinThread class to suspend
and resume the thread, respectively, while your application is running. The pointer also enables you to check the status of the thread.
NOTE
All the macros and constants required by the processes and threads are defined in the winbase.h header file. Therefore, you need to include this header file for the application using threads.