- Table of Contents
- Copyright
- About the Authors
- About the Contributors
- Acknowledgments
- Tell Us What You Think!
- Introduction
- How to Use This Book
- What You Need to Use This Book
- What's New in Visual C++ 6.0
- Contacting the Main Author
- Part I: Introduction
- Chapter 1. The Visual C++ 6.0 Environment
- Part II: MFC Programming
- Chapter 2. MFC Class Library Overview
- Chapter 3. MFC Message Handling Mechanism
- Chapter 4. The Document View Architecture
- Chapter 5. Creating and Using Dialog Boxes
- Chapter 6. Working with Device Contexts and GDI Objects
- Chapter 7. Creating and Using Property Sheets
- Chapter 8. Working with the File System
- Chapter 9. Using Serialization with File and Archive Objects
- Part III: Internet Programming with MFC
- Chapter 10. MFC and the Internet Server API (ISAPI)
- Chapter 11. The WinInet API
- Chapter 12. MFC HTML Support
- Part IV: Advanced Programming Topics
- Chapter 13. Using the Standard C++ Library
- Chapter 14. Error Detection and Exception Handling Techniques
- Chapter 15. Debugging and Profiling Strategies
- Chapter 16. Multithreading
- Chapter 17. Using Scripting and Other Tools to Automate the Visual C++ IDE
- Part V: Database Programming
- Chapter 18. Creating Custom AppWizards
- Chapter 19. Database Overview
- Chapter 20. ODBC Programming
- Chapter 21. MFC Database Classes
- Chapter 22. Using OLE DB
- Chapter 23. Programming with ADO
- Part VI: MFC Support for COM and ActiveX
- Chapter 24. Overview of COM and Active Technologies
- Chapter 25. Active Documents
- Chapter 26. Active Containers
- Chapter 27. Active Servers
- Chapter 28. ActiveX Controls
- Part VII: Using the Active Template Library
- Chapter 29. ATL Architecture
- Chapter 30. Creating COM Objects Using ATL
- Chapter 31. Creating ActiveX Controls Using ATL
- Chapter 32. Using ATL to Create MTS and COM+ Components
- Part VIII: Finishing Touches
- Chapter 33. Adding Windows Help
- Part IX: Appendix
Using Worker Threads
Worker threads are handy for any time you want to do something such as calculations or background printing. Worker threads also are useful when you need to wait on an event to occur, such as receiving data from another application, without forcing the user to wait. Let's face it—most users are not known for their patience.
Creating a worker thread is relatively simple; the hard part comes later, when you need to make sure that your thread plays well with others—but more on that later, in the section, "Thread Synchronization." To get a worker thread up and running, you implement a function that will be run in the thread, and then create the thread with AfxBeginThread(). Although you may choose to create your own CWinThread-based class, this is not necessary for worker threads.
Starting the Thread
An MFC thread, whether a worker or user-interface thread, is started with a call to AfxBeginThread(). This function is overloaded to handle the creation of the two flavors of threads, but for now, let's look at the variety used to create worker threads. Here is the prototype for this function:
CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc,
LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0, DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );
The first parameter is a pointer to the function that will run inside the thread. As you will soon see, this function can take a single parameter, which is passed as the second parameter. Generally, this is a pointer to a data structure of some sort.
Each thread also may have its own priority. This parameter may be set to any of the values accepted by SetThreadPriority(), which is discussed later.
Because each thread executes independently, it must have its own stack to keep track of function calls and the like. The size of the stack may be specified in the call to AfxBeginThread().
In most cases, you probably will want your thread to start doing its thing right off the bat. However, you may specify the CREATE_SUSPENDED flag in the dwCreateFlags parameter to create a thread that is suspended upon creation. This thread will not begin executing until ResumeThread() is called.
Optionally, you may specify a SECURITY_ATTRIBUTES structure to specify security parameters to be used with the thread.
AfxBeginThread() returns a pointer to the newly created CWinThread object. You squirrel this away somewhere so that you can work with the member functions of CWinThread later.
When you call AfxBeginThread(), it creates a new CWinThread object for you and calls its CreateThread() member function. At this point, unless you have specified CREATE_SUSPENDED, your new thread begins to execute the function you specified, and the thread that called AfxBeginThread() goes on its merry way.
The new thread continues to execute the function specified until that function returns, or until you call AfxEndThread() from within the thread. The thread also terminates if the process it is running in terminates.
Implementing a Thread Function
The sole purpose in life for a worker bee is to make honey. The sole purpose in life for a worker thread is to run its thread function, or controlling function, as Microsoft calls it in its documentation. In general, when the thread starts, this function starts. When the function dies, the thread dies.
First, your thread function should have a prototype that looks like this:
UINT MyThreadProc(LPVOID pParam);
All thread functions take a single 32-bit argument. Although you could pass a single value here, such as an int, it generally is more useful to pass a pointer to a structure or other object that can hold more information. This structure also may be used to return information to the rest of your application.
You could use the following simple thread function to encrypt a string, for example:
UINT MyThreadProc(LPVOID pParam)
{
if(pParam == NULL)
AfxEndThread(MY_NULL_POINTER_ERROR);
char *pStr = (char *)pParam;
while(*pStr)
*pStr++ ^= 0xA5;
return 0;
}
You could use this function in a thread created like this:
AfxBeginThread(MyThreadProc, pMySecretString);
After the thread is created, it starts executing until it either discovers that the pointer passed to it is null, or it finishes with the string. In either case, whether the function calls AfxEndThread() or simply returns, the function stops executing, its stack and other resources are deallocated, and the CWinThread object is deleted.
Accessing a Thread's Return Code
The exit code specified when the function returns or calls AfxEndThread() may be accessed by other threads in your application with a call to ::GetExitCodeThread(), which takes a handle to the thread and a pointer to a DWORD that will receive the exit code. The handle to the thread is contained in the m_hThread member of CWinThread, so it should be no problem to pass this to ::GetExitCodeThread(), right?
Well, there's a catch: By default, the CWinThread object is deleted as soon as the function returns or calls AfxEndThread(). You can get around this in one of two ways.
First, you can set the m_bAutoDelete member of CWinThread to FALSE, which prevents MFC from deleting the object automatically. You then can access the m_hThread member after the thread terminates. However, you now are responsible for deleting the CWinThread object.
Alternatively, you can use ::DuplicateHandle() to create a copy of the m_hThread member after the thread is created. You must be certain that you copy the handle before the thread terminates, however. The only way to be absolutely certain of this is to specify CREATE_SUSPENDED when the thread is created, copy the handle, then call ResumeThread() to start the thread. As you can see, this process gets to be a bit involved; thus, it generally is preferable to change the m_bAutoDelete member.
The exit code value returned by GetExitCodeThread() contains STILL_ACTIVE if the thread is still running, or if the thread has terminated, the return code that the thread passed when it returned or called AfxEndThread(). Note that STILL_ACTIVE is defined as 0x103 (259 decimal), so avoid using this as a return code from your thread.
Using User-Interface Threads | Next Section

Account Sign In
View your cart