2013-08-27 91 views
0

我正在MFC中使用后台工作线程(通过_beginthreadex创建)和UI线程编写应用程序。从UI线程中单击一个按钮以开始和结束工作线程。如果m_threadRunning标志为false,它将启动后台线程,如果它为true,则停止后台线程。我关于停止线程的方式是将m_threadRunning标志设置为false,并调用WaitForSingleObject让后台线程完成正在执行的操作。MFC应用程序挂起线程发信号通知终止

我的应用程序有四种不同的状态。我有前三个州正常工作,添加第四个州是什么导致我的问题。对于第四个状态,我希望能够对桌面进行采样并将平均RGB值发送到COM端口进行处理。当处于前三种状态时,如果我想停止向COM端口发送数据的执行,它将正常终止并且没有问题。如果我处于第四状态并单击“停止”,则应用程序将挂起,因为我没有时间拨打WaitForSingleObject

我也有一个自定义CEditCColorEdit,它显示当前的RGB值。当我处于状态3或4时,我会从后台线程更新它(因为它们都动态地更改颜色)。当我将颜色设置为InvalidateRedrawWindow时,我已将问题范围缩小至打电话。

我已经想出了一些解决方案,但我不喜欢它们中的任何一个,并且宁可理解是什么导致了这个问题,因为我在MFC中编写这个目标的目的是学习和理解MFC。这是什么已经解决了这个问题:

  1. 我在我的工作线程中调用睡眠()已经在约60采样/秒。将其更改为较低的值(例如每秒30个采样点),大多数时间都解决了问题。
  2. 我在我的工作线程中轮询m_threadRunning以检查线程是否应该终止。如果我在对屏幕进行采样后但在更新编辑控件之前对其进行轮询,则可以在大多数情况下解决问题。
  3. 我在调用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; 
} 

任何帮助,您可以提供非常感谢!

回答

3

当工作线程尝试访问在主线程中创建的控件并且主线程在WaitForSingleObject中挂起时,会发生死锁。只有在主线程接受关联消息给控件时,才能继续从工作线程更新控件。

从工作线程中删除对控件的所有访问。相反,PostMessage将自定义消息添加到主线程的窗口中。一个例子是在这里:

http://vcfaq.mvps.org/mfc/12.htm

同样的技术可以用来通知主线程的工作线程已完成,所以你能避免WaitForSingleObject的。

+0

谢谢!我认为这可能是这样的,我熟悉调用Invoke on控件的C#方法,但甚至没有想到它的MFC。 –