Multithreading: Creating Worker Threads

OverviewHow Do ISample

A worker thread is commonly used to handle background tasks that the user shouldn’t have to wait for to continue using your application. Tasks such as recalculation and background printing are good examples of worker threads. This article details the steps necessary to create a worker thread. Topics include:

  • Starting the thread

  • Implementing the controlling function

  • Example

Creating a worker thread is a relatively simple task. Only two steps are required to get your thread running: implementing the controlling function and starting the thread. It is not necessary to derive a class from . You can if you need a special version of CWinThread, but it is not required for most simple worker threads. You can use CWinThread without modification.

Starting the Thread

There are two overloaded versions of AfxBeginThread: one for user-interface threads and one for worker threads. To begin execution of your worker thread, call providing the following information:

  • The address of the controlling function.

  • The parameter to be passed to the controlling function.

  • (Optional) The desired priority of the thread. The default is normal priority. For more information on the available priority levels, see in the Win32 Programmer’s Reference.

  • (Optional) The desired stack size for the thread. The default is the same size stack as the creating thread.

  • (Optional) CREATE_SUSPENDED if you want the thread to be created in a suspended state. The default is 0, or start the thread normally.

  • (Optional) The desired security attributes. The default is the same access as the parent thread. For more information on the format of this security information, see in the Win32 Programmer’s Reference.

AfxBeginThread creates and initializes a CWinThread object for you, starts it, and returns its address so you can refer to it later. Checks are made throughout the procedure to make sure all objects are deallocated properly should any part of the creation fail.

Implementing the Controlling Function

The controlling function defines the thread. When this function is entered, the thread starts, and when it exits, the thread terminates. This function should have the following prototype:

UINT MyControllingFunction( LPVOID pParam );

The parameter is a single 32-bit value. The value the function receives in this parameter is the value that was passed to the constructor when the thread object was created. The controlling function can interpret this value in any manner it chooses. It can be treated as a scalar value, or a pointer to a structure containing multiple parameters, or it can be ignored. If the parameter refers to a structure, the structure can be used not only to pass data from the caller to the thread, but also to pass data back from the thread to the caller. If you use such a structure to pass data back to the caller, the thread will need to notify the caller when the results are ready. For information on communicating from the worker thread to the caller, see the article Multithreading: Programming Tips.

When the function terminates, it should return a UINT value indicating the reason for termination. Typically, this exit code is 0 to indicate success with other values indicating different types of errors. This is purely implementation dependent. Some threads may maintain usage counts of objects, and return the current number of uses of that object. To see how applications can retrieve this value, see the article Multithreading: Terminating Threads.

There are some restrictions on what you can do in a multithreaded program written with the Microsoft Foundation Class Library. For descriptions of these restrictions and other tips on using threads, see the article Multithreading: Programming Tips.

Controlling Function Example

This example shows how to define a controlling function and use it from another portion of the program.

UINT MyThreadProc( LPVOID pParam )
{
    CMyObject* pObject = (CMyObject*)pParam;

    if (pObject == NULL ||
        !pObject->IsKindOf(RUNTIME_CLASS(CMyObject)))
    return 1;   // if pObject is not valid

    // do something with 'pObject'

    return 0;   // thread completed successfully
}

// inside a different function in the program
.
.
.
pNewObject = new CMyObject;
AfxBeginThread(MyThreadProc, pNewObject);
.
.
.

What do you want to know more about?