我正在使用后台工作线程(通过_beginthreadex
创建)和UI线程在MFC中编写应用程序。从UI线程单击一个按钮以开始和结束工作线程。如果m_threadRunning
标志为false,则启动后台线程,如果为真,则停止后台线程。我停止线程的方法是将m_threadRunning
标志设置为false并调用WaitForSingleObject
让后台线程完成它正在做的事情。
我的应用有四种不同的状态。我让前三个状态正常工作,并添加第四个状态是导致我的问题的原因。对于第四种状态,我希望能够对桌面进行采样并将平均RGB值发送到COM端口进行处理。当处于前三个状态中的任何一个状态时,如果我想停止执行向COM端口发送数据,它将正常终止并且没有问题。如果我处于第四状态并单击“停止”,则应用程序将挂起,因为我没有时间打电话给WaitForSingleObject
。
我还有一个显示当前RGB值的自定义CEdit
框CColorEdit
。当我处于状态3或4时(因为它们都动态地改变颜色),我从后台线程更新它。我已将问题范围缩小到调用当我设置调用Invalidate
或RedrawWindow
时的颜色时。
我已经提出了一些解决方案,但我不喜欢它们中的任何一种,并且宁愿了解导致问题的原因,因为我在MFC中编写此目标的目的是学习和理解MFC。以下是解决问题的方法:
m_threadRunning
以检查线程是否应该终止。如果我在对屏幕进行采样后但在更新编辑控件之前对其进行轮询,则可以在大多数时间解决问题。WaitForSingleObject
时执行5秒超时,并在无法等待时调用TerminateThread手动终止该线程,这一直解决了问题。这是我现在的解决方案。以下是相关的代码位(我锁定任何outBytes的使用):
void CLightControlDlg::UpdateOutputLabel()
{
CSingleLock locker(&m_crit);
locker.Lock();
m_outLabel.SetColor(outBytes[1], outBytes[2], outBytes[3]); //the call to this freezes the program
CString str;
str.Format(L"R = %d; G = %d; B = %d;", outBytes[1], outBytes[2], outBytes[3]);
m_outLabel.SetWindowText(str);
}
此部分代码用于终止工作线程
m_threadRunning = false;
locker.Unlock(); //release the lock...
//omitted re-enabling of some controls
//normally this is just WaitForSingleObject(m_threadHand, INFINITE);
if(WaitForSingleObject(m_threadHand, 5000) == WAIT_TIMEOUT)
{
MessageBox(L"There was an error cancelling the I/O operation to the COM port. Forcing a close.");
TerminateThread(m_threadHand, 0);
}
CloseHandle(m_threadHand);
CloseHandle(m_comPort);
m_threadHand = INVALID_HANDLE_VALUE;
m_comPort = INVALID_HANDLE_VALUE;
我的派生编辑控件中的代码,用于更新文本颜色:
void SetColor(byte r, byte g, byte b)
{
_r = r;
_g = g;
_b = b;
br.DeleteObject();
br.CreateSolidBrush(RGB(r,g,b));
Invalidate(); //RedrawWindow() freezes as well
}
最后,我的线程程序的代码:
unsigned int __stdcall SendToComProc(void * param)
{
CLightControlDlg *dlg = (CLightControlDlg*)param;
while(1)
{
if(!dlg->IsThreadRunning())
break;
switch(dlg->GetCurrentState())
{
case TransitionColor: //state 3
dlg->DoTransition();
dlg->UpdateOutputLabel();
break;
case ScreenColor: //state 4
dlg->DoGetScreenAverages();
//if(!dlg->IsThreadRunning()) break; //second poll to IsThreadRunning()
dlg->UpdateOutputLabel();
break;
}
dlg->SendToCom();
Sleep(17); // Sleep for 1020 / 60 = 17 = ~60samples/sec
}
return 0;
}
非常感谢您提供的任何帮助!
答案 0 :(得分:3)
当工作线程尝试访问在主线程中创建的控件并且主线程在WaitForSingleObject中挂起时,会出现死锁。只有当主线程接受到控件的关联消息时,才能从工作线程更新控件。
从工作线程中删除对控件的所有访问。而是将PostMessage自定义消息发送到主线程中的窗口。这里有一个例子:
http://vcfaq.mvps.org/mfc/12.htm
可以使用相同的技术来通知主线程工作线程已完成,这样就可以避免WaitForSingleObject。