I'm having a problem with a thread construction.
I have 2 buttons in a dialog box.
Pressing 'start' will activate the thread.
Pressing 'stop' has to terminate the thread.
I'm using an event (m_Finished) to indicate that the action is
started.
When the thread returns, I set that event, so I know the action is
done.
Pressing the 'close' button, the application must quit, BUT the thread
must be finished first. I'm using the 'm_Finished' event to see is the
action is done. If it's still busy, I want the thread to be terminated
before closing, so the OnClose() function has to wait until the thread
is done.
// ************************************************
// Simple thread that does ... nothing, except wait
// for an 'ending' command (passed by an event).
// ************************************************
UINT TestProc(LPVOID pParam)
{
HANDLE hEvent = *(HANDLE*)pParam;
bool bThreadDone = false;
while (!bThreadDone)
{
DWORD dwResult = ::WaitForSingleObject(hEvent, 500);
if (dwResult == WAIT_OBJECT_0)
bThreadDone = true;
}
return 0;
}
// ************************************************
// Thread that has to block execution in OnClose()
// until 'm_Finished' event is set.
// ************************************************
UINT WaitUntilFinished(LPVOID pParam)
{
HANDLE hEvent = *(HANDLE*)pParam;
bool bLoop = true;
while (bLoop == true)
{
DWORD dwResult = WaitForSingleObject(hEvent, 500);
if (dwResult == WAIT_OBJECT_0)
bLoop = false;
}
return 0;
}
// *****************************
// if "START"-button is pressed
// --> start thread
// *****************************
void CtestDlg::OnBnClickedStart()
{
ResetEvent(m_Finished); // <--- m_Finished in nonsignaled state
CWinThread* pThread = AfxBeginThread(TestProc, &m_TerminateAsked);
DWORD dwRet;
do
{
dwRet = WaitForSingleObject(pThread->m_hThread, 100);
if (dwRet != WAIT_OBJECT_0)
{
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
} while ((dwRet != WAIT_OBJECT_0) && (dwRet != WAIT_FAILED));
SetEvent(m_Finished); // <--- m_Finished in signaled state
}
// *****************************
// if "STOP"-button is pressed
// *****************************
void CtestDlg::OnBnClickedStop()
{
SetEvent(m_TerminateAsked);
}
// *****************************
// if "CLOSE"-button is pressed
// *****************************
void CtestDlg::OnClose()
{
SetEvent(m_TerminateAsked);
// before closing the dialog box, all action must be done
// ---> m_Finished event must have been set
CWinThread* pThread = AfxBeginThread(WaitUntilFinished,
&m_Finished);
WaitForSingleObject(pThread->m_hThread, INFINITE);
CloseHandle(m_Finished);
CloseHandle(m_TerminateAsked);
CDialog::OnClose();
}
Using the sequence 'start' -> 'stop' -> 'close' works.
Using the sequence 'start' -> 'close' should also be working, but it
doesn't !
For some reason, I believe WaitForSingleObject() in the OnClose()
function is
blocking the whole thing cause the 'm_Finished' event is never set.
It is waiting until the m_Finished event gets in the signaled state.
That's exactly what I want, BUT ... for some reason, the
WaitForSingleObject()
in the OnBnClickedStart() function is blocked until the wait function
in the
WaitUntilFinished() is done. And that isn't possible since that
function is
waiting on the m_Finished event.
So ... I would like the WaitUntilFinished()-thread to block the
processing of
the OnClose()-function until the m_Finished event is set, but in the
meanwhile,
the other processing must continue (e.g. setting the m_Finished event
in OnBnClickedStart),
otherwise, the system will hang.
Any idea's ?
Jase
"PS" <abc_3...@hotmail.com> wrote in message
news:57a6d9b.04031...@posting.google.com...
Do you reckon, if you tried, could you make it any more complicated? You've
really confused the issue by putting a message loop in a command handler.
It's actually *heaps* simpler than that. You only need 3 lines of code to
start and stop the thread.
In the start button handler:
m_pThread = AfxBeginThread(TestProc, &m_TerminateAsked);
where m_pThread is a CWinThread * member variable.
In the stop button handler::
SetEvent(m_TerminateAsked);
WaitForSingleObject(m_pThread->m_hThread, INFINITE);
If you use a thread proc that might terminate itself, then you need 3 more
lines:
Add this to the start button handler:
m_pThread->m_bAutoDelete = false;
In the stop button handler, wrap the calls in
if (GetExitCodeThread(m_pThread->m_hThread) == STILL_ACTIVE) {
...
}
Jase
--------------------------------------------------------
// ********
// function
// ********
void CtestDlg::ExecFunction()
{
m_pThread = AfxBeginThread(TestProc, &m_TerminateAsked);
}
// ********
// Start
// ********
void CtestDlg::OnBnClickedStart()
{
ExecFunction();
}
// ********
// Stop
// ********
void CtestDlg::OnBnClickedStop()
{
DWORD ExitCode;
GetExitCodeThread(m_pThread->m_hThread, &ExitCode);
if (ExitCode == STILL_ACTIVE)
{
SetEvent(m_TerminateAsked);
WaitForSingleObject(m_pThread->m_hThread, INFINITE);
}
}
// ********
// Close
// ********
void CtestDlg::OnClose()
{
DWORD ExitCode;
GetExitCodeThread(m_pThread->m_hThread, &ExitCode);
if (ExitCode == STILL_ACTIVE)
{
SetEvent(m_TerminateAsked);
WaitForSingleObject(m_pThread->m_hThread, INFINITE);
}
CloseHandle(m_TerminateAsked);
CDialog::OnClose();
}
// ********
// Thread
// ********
UINT TestProc(LPVOID pParam)
{
HANDLE hEvent = *(HANDLE*)pParam;
int ctr = 0;
bool bThreadDone = false;
while (!bThreadDone)
{
DWORD dwResult = WaitForSingleObject(hEvent, 500);
if (dwResult == WAIT_OBJECT_0)
bThreadDone = true;
ctr++;
if (ctr > 10)
bThreadDone = true;
}
return 0;
}
--------------------------------------------------------
But now I have another question, If you're experienced with threads you may know
the answer.
Somehow, it must be possible to 'schedule' multiple functions.
note : this is a simplified example, the real program will pass some parameters
with ExecFunction() !
void CtestDlg::OnBnClickedStart()
{
ExecFunction(...); // first
ExecFunction(...); // second
ExecFunction(...); // third
}
void CtestDlg::ExecFunction()
{
// here, a construction must prevent the thread from being started if :
// a) the previous thread is still running
// b) 'm_TerminateAsked' is in signaled state
m_pThread = AfxBeginThread(TestProc, &m_TerminateAsked);
}
The ExecFunction(...)'s in OnBnClickedStart() must be executed sequentially
(after each other, not at the same time !).
A new ExecFunction() is allowed to be started after the previous one has been
terminated.
If -during a thread execution- the m_TerminateAsked gets in the signaled state,
the active thread must be terminated, and the threads that were waiting to be
started must be canceled.
I don't succeed in creating a software construction that doesn't make the system
hang.
Can anyone help me please ?
You need a different approcah to avoid hanging in the GUI thread. The
GUI thread must keep processing messages. It shouldn't block in a
WaitFor... call. So the whole program, and especially the three calls
to execute things sequentially, should be event-driven, not three calls
in series.
You can set up logic to launch the first thread. When it has finished
it can cause the second to start, and etc. There are two ways: posting
messages or setting events. Posting messages is the only way to do this
if the GUI thread is involved. (Setting events is the only way to do
this if overlapped I/O is involved.) Look here to see how to post
messages to yourself, including between threads:
http://www.mvps.org/vcfaq/mfc/index.htm
Your thread code can sense events (is that what m_TerminateAsked is?) by
calling WaitForSingleObject with a 0 timeout. The return value
indicates whether or not the event is signaled.
--
Scott McPhillips [VC++ MVP]
If I may butt in (I admit I didn't read the rest of the thread carefully),
there's MsgWaitForMultipleObjects API which can help you construct
a message loop with event waiting; I'm having in mind something like:
HANDLE hEvent[3];
hEvent[0] = CreateEvent(..."EventThread1")
hEvent[1] = CreateEvent(..."EventThread2")
hEvent[2] = CreateEvent(..."EventThread3")
for(;;)
{
iRet = MsgWaitForMultipleObjects(3, hEvent, FALSE, INFINITE, QS_ALLEVENTS);
if (iRet >= WAIT_OBJECT_0 && iRet < WAIT_OBJECT_0 + 2)
{
!An event is signalled
}
else
{
!A message was posted
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != WM_QUIT)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
!Clean up here
break;
}
}
}
Since you want a simpler scheme of sequential execution, waiting on only
*one* (instead of 3) hEvent (and/or a hThread) and reusing it subsequently
could be a simpler scheme than the one above.
(Disclaimer: the code above is untested & written off the top of my head;
I meant only to illustrate the principle rather than to provide a ready
solution).
--
Jugoslav
___________
www.geocities.com/jdujic
Please reply to the newsgroup.
You can find my real e-mail on my home page above.