Re: Thread Deadlock Problem.
First, I have to say I never though I would generate that kind of debate for
asking a question...
Second, forgive me to not give you snippets and forgive me to come from a
Assembly/ C low level programming background...
To answer some of your questions.
1- No , I didn`t create my thread suspended from what I have read on MSDN it
didn`t look mandatory, I thought calling AfxEndThread(0, FALSE); was enough
to not delete the object automatically. Should I create the thread
suspended and change m_bAutoDelete to FALSE before running the thread
instead?
2- I used an auto-reset event because I wanted the event to be treated only
once. Using a manual reset event could get this section of code done
several times since I was using asynchronous threads.
Finaly, before showing you what I was doing in snippets. I will give what I
wanted to do. My main thread is used like ping on a serial communication,
the ping is sent each 5 seconds. The second thread is used as a seperate
timer (each minute) and notify the main thread to execute an optionnal
operation. The callbacks were used to give an update of the communication
status to the dialog box. If we want to do it the good way how should I
send the updated status to the dialog?
Here is the snippets of my utility class.
__________
bool CNWCManager::OpenConnection(int ComPort)
{
....
m_hThreadEvent = CreateEvent(NULL, FALSE, FALSE,
_T("KillUpdateLampThread"));
if(m_hThreadEvent != NULL)
{
//Start Thread
m_pUpdateThread = AfxBeginThread(UpdateThread,this);
if(m_pUpdateThread != NULL)
{
if(m_IsMeteringEnabled == true)
{
EnableMetering(true);
}
}
else
{
SetEvent(m_hThreadEvent);
if(WaitForSingleObject(m_pUpdateThread->m_hThread,INFINITE) ==
WAIT_OBJECT_0)
{
delete m_pUpdateThread;
}
}
}
bool CNWCManager::CloseConnection(void)
{
bool bResult = false;
//End Thread
if(m_hThreadEvent != NULL)
{
SetEvent(m_hThreadEvent);
if(WaitForSingleObject(m_pUpdateThread->m_hThread,INFINITE) ==
WAIT_OBJECT_0)
{
delete m_pUpdateThread;
m_pUpdateThread = NULL;
}
if(m_hMeteringThreadEvent != NULL)
{
SetEvent(m_hMeteringThreadEvent);
if(WaitForSingleObject(m_pMeteringThread->m_hThread,INFINITE) ==
WAIT_OBJECT_0)
{
delete m_pMeteringThread;
m_pMeteringThread = NULL;
}
}
bResult = true;
}
//Close COM port
CloseHandle(m_hComm);
return bResult;
}
UINT CNWCManager::UpdateThread(LPVOID pParam)
{
CNWCManager* pObject = (CNWCManager*) pParam;
HANDLE hEndThread = CreateEvent(NULL, FALSE, FALSE,
_T("KillUpdateLampThread"));
HANDLE hMeteringEvent = CreateEvent(NULL, FALSE, FALSE,
_T("MeteringEvent"));
HANDLE hEventArray[2];
hEventArray[0] = hEndThread;
hEventArray[1] = hMeteringEvent;
DWORD EventStatus;
bool ExitThread = false;
do
{
EventStatus = WaitForMultipleObjects(2, hEventArray, FALSE, 5000);
switch(EventStatus)
{
case WAIT_OBJECT_0:
ExitThread = true;
break;
case WAIT_OBJECT_0 + 1:
pObject->MeteringUpdate();
break;
case WAIT_TIMEOUT:
pObject->SendUpdate();
break;
case WAIT_FAILED:
ExitThread = false;
break;
default:
ExitThread = false;
break;
}
}
while(ExitThread == false);
CloseHandle(hEndThread);
CloseHandle(hMeteringEvent);
AfxEndThread(0, FALSE);
return 0;
}
UINT CNWCManager::MeteringTimerThread (LPVOID pParam)
{
CNWCManager* pObject = (CNWCManager*) pParam;
bool ExitThread = false;
DWORD EventStatus;
HANDLE hEndThread = CreateEvent(NULL, FALSE, FALSE,
_T("KillMeteringThread"));
HANDLE hMeteringEvent = CreateEvent(NULL, FALSE, FALSE,
_T("MeteringEvent"));
do
{
EventStatus = WaitForSingleObject(hEndThread, 60000);
switch(EventStatus)
{
case WAIT_OBJECT_0:
ExitThread = true;
break;
case WAIT_TIMEOUT:
SetEvent(hMeteringEvent
);
break;
case WAIT_FAILED:
ExitThread = false;
break;
default:
ExitThread = false;
break;
}
}
while(ExitThread == false);
CloseHandle(hEndThread);
AfxEndThread(0, FALSE);
return 0;
}
void CNWCManager::MeteringUpdate (void)
{
m_CommunicationMutex.Lock();
//Callback to dialog for updating the metering values
m_pCallback[1]->Execute(NULL);
m_CommunicationMutex.Unlock();
}
void CNWCManager::EnableMetering(bool bIsEnable)
{
//Create Timer Thread for metering
if(bIsEnable == true)
{
if(m_hMeteringThreadEvent == NULL)
{
m_hMeteringThreadEvent = CreateEvent(NULL, FALSE, FALSE,
_T("KillMeteringThread"));
}
if(m_pMeteringThread == NULL)
{
m_pMeteringThread = AfxBeginThread(MeteringTimerThread,this);
}
m_IsMeteringEnabled = true;
}
else //Kill Thread
{
if(m_hMeteringThreadEvent != NULL && m_pMeteringThread != NULL)
{
SetEvent(m_hMeteringThreadEvent);
if(WaitForSingleObject(m_pMeteringThread->m_hThread,INFINITE) ==
WAIT_OBJECT_0)
{
delete m_pMeteringThread;
m_pMeteringThread = NULL;
}
CloseHandle(m_hMeteringThreadEvent);
m_hMeteringThreadEvent = NULL;
m_IsMeteringEnabled = false;
}
}
}
in the threads debug window I have 3 threads, the main, CWnd:UpdateWindow()
and my MeteringTimerThread.
callstack for CWnd::UpdateWindow
ntdll.dll!76ef64f4()
[Frames below may be incorrect and/or missing, no symbols loaded for
ntdll.dll] user32.dll!77024341()
user32.dll!77022bfe()
TLAC Demo.exe!CWnd::UpdateWindow() Line 142 + 0x39 bytes C++
TLAC Demo.exe!CFontStatic::RedrawFont() Line 259 C++
TLAC Demo.exe!CTLACDemoDlg::CallbackUpdateStatus(void * Param=0x015982b4)
Line 668 C++
TLAC Demo.exe!TCallback<CTLACDemoDlg>::Execute(void * Param=0x015982b4)
Line 30 + 0x1d bytes C++
TLAC Demo.exe!CNWCManager::SendUpdate() Line 399 + 0x1b bytes C++
TLAC Demo.exe!CNWCManager::UpdateThread(void * pParam=0x01598270) Line
210 C++
TLAC Demo.exe!_AfxThreadEntry(void * pParam=0x0012e654) Line 109 + 0xf
bytes C++
TLAC Demo.exe!_callthreadstartex() Line 348 + 0xf bytes C
TLAC Demo.exe!_threadstartex(void * ptd=0x0159a788) Line 331 C
kernel32.dll!76621194()
ntdll.dll!76f0b3f5()
ntdll.dll!76f0b3c8()
callstack for CNWCManager::MeteringTimerThread
[Frames below may be incorrect and/or missing, no symbols loaded for
ntdll.dll] ntdll.dll!76ef5e6c()
KernelBase.dll!750b179c()
kernel32.dll!7661f003()
kernel32.dll!7661efb2()
TLAC Demo.exe!CNWCManager::MeteringTimerThread(void * pParam=0x01598270)
Line 281 + 0x11 bytes C++
TLAC Demo.exe!_AfxThreadEntry(void * pParam=0x0012e688) Line 109 + 0xf
bytes C++
TLAC Demo.exe!_callthreadstartex() Line 348 + 0xf bytes C
TLAC Demo.exe!_threadstartex(void * ptd=0x01591cb0) Line 331 C
kernel32.dll!76621194()
ntdll.dll!76f0b3f5()
ntdll.dll!76f0b3c8()
______________
That is the essential of my class. The dialog calls openconnection and
closeconnection to start the threads and terminate them. EnableMetering is
used to activate the second thread for the optional MeteringUpdate () call
in UpdateThread();
Thank you.