Worker Threads

Shutting down a thread from a view or main frame

There is sometimes a problem in shutting down a thread. If you don't do things in the right order, you could even shut down your GUI thread while the worker thread is still running, which can lead to all sorts of interesting problems. Interesting, as in the traditional Chinese curse. So here's a method I've used to shut down a thread a be sure it is shut down before the view is shut down.

First, you must store a pointer to the CWinThread object in your view, so declare

CWinThread * myWorkerThread;

in your view. When you create the worker thread, create it as

     myWorkerThread = AfxBeginThread(run, this);

You will need this variable to synchronize the shutdown with the view termination.

void CMyView::OnClose()
    {
     // ... determine if we want to shut down
     // ... for example, is the document modified?
     // ... if we don't want to shut down the view, 
     // ... just return
  
     // If we get here, are are closing the view
     myWorkerThread->m_bAutoDelete = FALSE;
     running = FALSE;
     WaitForSingleObject(myWorkerThread->m_hThread, INFINITE);
     delete myWorkerThread;
     CView::OnClose(); // or whatever the superclass is
    }

The only odd thing that appears in the previous function is the saving of the m_bAuthoDelete flag explicitly to FALSE. This is because the deletion of the CWinThread-derived object can close the handle of the thread, rendering the subsequent WaitForSingleObject invalid. By inhibiting the auto-deletion, we can wait on the thread handle. We then do the explicit deletion of the CWinThread-derived object ourselves, since it is now no longer useful.

Special thanks to Charles Doucette for pointing out a flaw in my original article which he found in another essay by Doug Harrison: there was a race condition; I had previously stored the handle and shut down the thread. But the auto-delete invalidated the handle which could lead to incorrect behavior.  

By storing the handle to a variable, we can then do a WaitForSingleObject on the thread. The close operation then blocks until the thread terminates. Once the thread has terminated, we can proceed with the close by calling our superclass OnClose handler (in this example, we are a derived class of CView).

There is a caution here: this assumes that the thread will actually terminate "within a reasonable time". If you have a thread that is blocked on I/O or a synchronization object, you will need to add a timeout mechanism as I have already described. Note that this prohibits the use of CRITICAL_SECTION as a synchronization object since they don't have a timeout capability. If you're blocked on a CRITICAL_SECTION you're stuck forever.

Of course, in the general case you may have several synchronization mechanisms that are necessary to ensure the thread will terminate within a reasonable period of time. A serious design flaw in the whole AfxBeginThread mechanism is that it doesn't allow me to create a CWinThread-derived subclass of my own which is the object created. In this case, I've sometimes subclassed CWinThread and bypassed the AfxBeginThread by doing my own thread creation inside my subclass, and exporting methods such as CMyWinThread::shutdown that do whatever is needed to make the thread shut down cleanly and quickly.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Anyone who considers arithmetic methods of producing random digits is, of course, in a state of sin.” - John von Neumann