Simple Thread: Part I

Simple Thread: Part I

Starting, pausing, resuming, and stopping threads in MFC.

Abstract

Frequently in the Visual C++ forum and multithreaded forum, questions are raised on how to start and stop threads and how to manipulate MFC UI elements from within a worker thread. This article will take the reader through the steps of building a simple MFC dialog application called StartStop. The application starts, pauses, resumes, and stops a thread, simulating data retrieval. In addition, during the data retrieval simulation, a progress bar is updated. Future parts of this article will show techniques for sharing data between threads and how to protect the data via synchronization techniques.

Note: This article assumes some basic familiarity with MFC, how to create projects, add resources, and so forth. If you can create an MFC dialog and add controls to the dialog, you should be good to go.

Creating the Basic Project

Open up Visual Studio .Net 2003 and create an MFC dialog project called StartStop.

Because it’s a good idea to keep the UI code separate from the threading code, you are going to put the threading code inside a ProgressMgr class, but first you are going to create the basic UI ‘shell’ code that creates the basic button handlers and UI variables that allow you to enable/disable the buttons, change the button text, and manipulate the progress control. You know, the UI stuff.

Create the UI

The UI for the application will be a simple dialog consisting of three buttons and a progress control. To create the UI, open the resource editor and click on the IDD_STARTSTOP_DIALOG. Next, delete the OK button and add two buttons with the ID of the first button ID_STARTPAUSERESUME and the second one as ID_STOP. Also, add a progress control. You should end up with something like Figure 1.

Figure 1

The plan will be to add some button and progress control variables, button message handlers, and some user-defined messages to let the UI to know when to update the progress control and when the thread has finished. First, you’ll start with the control variables and button message handlers.

Add the UI control variables

Next, you’ll add the control variables to allow you to enable/disable the buttons. Although many programmers use the Win32 GetDlgItem() API to connect the controls, it’s preferred to leverage MFC’s DDX mechanism and let MFC handle the plumbing of the controls. Once you’ve tried this method, you may never go back to GetDlgItem().

Creating controls with DDX in MFC is easy; you just highlight the control in the resource editor, right-click on the control, and choose ‘Add variable…’. Here’s how to do it for the Start button.

  1. Open the dialog in the resource editor.
  2. Right-click on the Start button and choose ‘Add variable…’.
  3. The ‘Add Member Variable Wizard bo?= StartStop’ dialog will appear (see Figure 2).
  4. Under ‘Variable name:’, ENTER m_btnStartPauseResume.
  5. Press Finish.

Figure 2

Repeat for the Stop button and Progress control

Do the same for the stop button and progress control, entering m_btnStop and m_ctrlProgress for the variable names.

Add the Start and Stop button message handlers

After creating the control variables, in the dialog editor just double-click on the Start, Stop, and Cancel buttons to create the message handlers. You should end up with empty handlers as follows:

void CStartStopDlg::OnBnClickedStartPauseResume()
{
   // TODO: Add your control notification handler code here
}
void CStartStopDlg::OnBnClickedStop()
{
   // TODO: Add your control notification handler code here
}
void CStartStopDlg::OnBnClickedCancel()
{
   // TODO: Add your control notification handler code here
   OnCancel();
}

Toggling the Start/Pause button state

You want the start button to also function as a pause and resume button. To do this, you need to keep track of what ‘mode’ the button is in. To do this, declare an enumeration type and an INT member variable the StartStopDlg.h file. Don’t forget to initialize this variable in the constructor to FALSE. You should end up with the following:

// Implementation (in StartStopDlg.h)

protected:
   enum ThreadStatus { TS_STOPPED, TS_START, TS_PAUSE, TS_RESUME };
   INT   m_ThreadState;

// CStartStopDlg dialog (in StartStopDlg.cpp)
CStartStopDlg::CStartStopDlg(CWnd* pParent    /*=NULL*/)
   : CDialog(CStartStopDlg::IDD, pParent)
   , m_ThreadState( TS_STOPPED )
{
   m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

Next, add a method to toggle the button display text and toggle state.

// Toggles the Start/Pause/Resume state and sets the button text
INT CStartStopDlg::ToggleSPRState( )
{
   if( TS_RESUME == m_ThreadState )
   {
      m_ThreadState = TS_START;
   }

   m_ThreadState++;

   CString sButtonText = _T("");

   switch( m_ThreadState )
   {
   case TS_START:
      sButtonText = _T("Pause");
      break;
   case TS_PAUSE:
      sButtonText = _T("Resume");
      break;
   case TS_RESUME:
      sButtonText = _T("Pause");
      break;
   default:
      ASSERT( 0 );    // We shouldn't reach this
   }

   // Set button text
   m_btnStartPause.SetWindowText( sButtonText );

   return m_ThreadState;
}

And a method to reset the StartPause button state and button text.

// Reset the start/pause button and state
void CStartStopDlg::ResetSPRState( )
{
   m_ThreadState = TS_STOPPED;
   m_btnStartPause.SetWindowText( _T("Start") );
   m_ctrlProgress.SetStep( 0 );
}

More by Author

Previous articleGet Jagged!
Next articleWhat’s New in C++00X?

Must Read