This article describes a method for preventing a deadlock that worker threads cause when calling User Interface methods. A deadlock occurs when the parent window thread is waiting for the worker thread to complete, while the worker thread is waiting for the parent window thread to handle its blocking User Interface call.
The user should have a prior knowledge of multi-threading programming in Windows.
The implementation (below) waits for the thread to complete. When a user interface message is in the message queue it is being handled, in order to avoid the deadlock described above.
WaitResult = MsgWaitForMultipleObjects(1,
m_hThread, FALSE, INFINITE, QS_ALLINPUT);
if (WaitResult == WAIT_OBJECT_0 +1)
BOOL IsMessage = ::PeekMessage( Msg,NULL,0,0,PM_REMOVE);
} while (WaitResult == WAIT_OBJECT_0+1);
ASSERT(WaitResult == WAIT_OBJECT_0);
Now, lets add another twist. Sometimes the parent user interface window would like to abort the thread immediately, and yet without any memory leaks. The implementation of the abort method raises a flag. The worker thread must call
eriodically to check this flag.
The cleanest way to avoid memory leaks is by throwing an exception. The exception throwing mechanism cleanly destructs the objects in the stack. Objects allocated using the new operator are deleted in the destructor (of one of the objects in the stack) or by a Smart Pointer. You should not worry about catching the exception.
has a wrapper of the thread procedure that catches this exception for you.
void CSafeThread::CheckAborted(DWORD WaitTime )
if (GetCurrentThread()!=NULL &&
GetCurrentThread()->m_AbortEvent.m_hObject,WaitTime) == WAIT_OBJECT_0)
The worker thread must also check the abort flag while it Sleeps or Waits. An implementation of
, does just that. Below is the implementation of
DWORD CSafeThread::WaitForSingleObject(HANDLE hHandle,
CSafeThread* pThis = GetCurrentThread();
if (pThis == NULL)
Handles = hHandle;
Handles = m_AbortEvent.m_hObject;
DWORD WaitResult = ::WaitForMultipleObjects(2,
if (WaitResult == WAIT_OBJECT_0 + 1)
In addition the
implementation stores the handle of the parent window and provides an implementation to
. Calling this static method ensures that the parent window will be disabled when the message box appears.
Using the code
The example counts down from 10 to 0, displays a message box, and closes the dialog box. In order to start a new safe thread use the supplied
BeginSafeThread static method. If you are going to use the
MessageBox static method, do not forget to supply the last parent window argument. In the header file, you may use the
tyepdef (SP stands for Smart Pointer) that will automatically delete the thread object when the dialog box is deleted. The SmartPtr class implementation was written by Sandu Turcan.
m_pThread = CSafeThread::BeginSafeThread(CountThread,
this, THREAD_PRIORITY_NORMAL, 0, 0, 0, this
The implementation uses a helper static method that converts the parameter to this and calls the
Count() method. The implementation of
Count() changes the value of
m_Counter (an MFC static text control) every one second.
UINT CSafeThreadExampleDlg::CountThread(void* pParam)
CSafeThreadExampleDlg* pThis =
for (int i =10 ; i >=0 ; i--)
CSafeThread::MessageBox("Countdown complete", "SafeThreadExample"); EndDialog(IDOK); }
The user interface of the example includes two buttons. The first one waits until the countdown completes, and the second one aborts the count down.
Points of Interest
I am not sure that Microsoft MFC supports the workaround presented in this article. It worked for my project, and I would like to know here if it works also for you