viksoe.dk

CThread


This article was submitted .


This class is a thin wrapper around the Win32 Thread API.
Since CPU performance no longer appear to take great leaps forward towards faster CPUs, but instead will include multiple cores on the same die, the use of multi-threading and parallel-processing in software applications will become increasingly important.

This is the thread C++ class I have been using for several years now. It's quite simple yet very effective.
You may find C++ thread classes included in various frameworks for both Windows and other platforms, but the thing about thread classes is that everyone seems to have different requirements for using threads so finding one you like - and can trust that it works - is difficult. So I wrote my own. This is the Windows version; I have a file with the same interfaces for UNIX and LINUX as well, but I cannot share those.

The file contains the following classes.

CThreadT
CThreadHandle
CThread
CThreadImpl
CEvent
CCriticalSection
CAutoCriticalSection
These are the basic tools for working with threads on Windows. Similar to what the WTL library does with the window APIs, the CThreadHandle and CThread simply wrap the Win32 Thread APIs. The latter will clean up the Win32 thread handle when the class instance exists scope.

The CThreadImpl class allows you to use threads in managed fashion. By default it will work as what I call a scoped thread. That means that you have some control over when the thread is running. This will be explained in the sample below, but think of the thread class as being designed for worker threads that you launch to do some time-consuming work for some code that otherwise runs in the main thread.

Using the code

Here is an example of creating a thread managed by a separate C++ class:
class CMyThread : public CThreadImpl<CMyThread>
{
public:
  int m_iLoops;

  DWORD Run()
  {
    int i = 0;
    while( !IsAborted() && ++i <= m_iLoops ) {
      ::Sleep(100); // Do stuff...
    }
    return 0;
  }
};
Notice how the thread class continously will call IsAborted to see if someone from the outside requested that the thread should cancel its processing and exit. It didn't have to do this, but being able to cancel a running worker thread can have some surprising benefits.
To start and run the thread, you can use the following code..
BOOL CMainWindow::OnButtonClick()
{
  CMyThread thread;     // Probably wrong scope
  thread.m_iLoops = 20;
  thread.Start();
  return TRUE;
}
The thread will run using the code above, but if you're writing a Windows application with a user-interface and the main thread is used to drive the Windows Message Pump, then this is a terrible example. The scoped thread class will wait for the thread to terminate when its C++ class instance exists scope, so what happens is that when the button-click function reaches...
return TRUE
...it will wait for the thread to complete its work before it exits the OnButtonClick function.
This is intentional because the thread member can be placed outside the function scope. Here is some better usage:
CMyThread m_thread;
...

BOOL CMainWindow::OnButtonClick()
{
  m_thread.Stop();
  m_thread.m_hWnd = m_hWndFrame;
  m_thread.m_iLoops = 20;
  m_thread.Start();
  // We could disable the button here
  return TRUE;
}
Calling the Stop function is just an extra precaution. The thread should never be running when you enter the button-click function in the first place. What the code snippet does is simply to make sure the thread isn't running, copy the data the thread should be working on from the main thread, and then launch it.
When copying data to the thread class, copy everything by-value. Don't be tempted to pass a pointer to data in the main thread's data area, because it will not be safe to access if mulitple threads are changing the same memory space. Instead copy as much as possible, and never pass references to UI objects (GDI handles, window handles, etc) unless you intent to access them purely in a thread-safe manner. For communication with a UI thread, that is by using the PostMessage API.

The thread class needs to be able to notify the main thread that it completed its job. The main thread (where the thread is scoped) in this example keeps our window running, and the window wants to display the result. Here is an example on how this can be done with a private window message. First the modified thread class.

class CMyThread : public CThreadImpl<CMyThread>
{
public:
  HWND m_hWnd;
  int m_iLoops;

  DWORD Run()
  {
    int i = 0;
    while( !IsAborted() && ++i <= m_iLoops ) {
      ::Sleep(100); // Do stuff...
    }
    // Send notification and exit
    if( ::IsWindow(m_hWnd) ) {
      ::PostMessage(m_hWnd, WM_APP + 100, 0, 0L);
    }
    return 0;
  }
};
The code behind the main window will eventually receive the message and interpret it. Use PostMessage rather than SendMessage to notify the main window. In response, the main window will extract and process the result.
...

LRESULT CMainWindow::OnWmApp100()
{
  int iResult = m_thread.m_iLoops;
  // Do something with iResult...
  // Enable the button again 
  return 0;
}

LRESULT CMainWindow::OnWmDestroy()
{
  m_thread.Abort();
  return 0;
}
Also shown in the above sample, is how the window don't care to wait when it's being closed and the long-running thread is still processing data. It simply asks the thread to exit as soon as possible so it can tear down the window.

This system is about the simplest you can safely construct. Of course you may find that communicating with Windows messages is not ideal for your particular solution, or that you need multiple tasks to run in a more advanced thread-pool setup.

Finally, if you just wish to launch a thread and don't care about its scope, you can do this...

BOOL OnButtonClick()
{
  CMyThread* pThread = new CMyThread;
  pThread->SetDeleteOnExit();
  pThread->m_iLoops = 20;
  pThread->Start();
}
The SetDeleteOnExit function will cause the thread class to launch, and eventually commit suicide by doing
delete this
...code when the thread terminates. When the thread has exited you may no longer reference the thread class' C++ members.

Source Code Dependencies

Microsoft Visual C++ 6.0

See Also

Using a threadpool in Windows Vista

Download Files

DownloadSource Code (4 Kb)

To the top