MFC应用程序在线程中挂起,表示已终止

时间:2013-08-27 00:11:38

标签: c++ multithreading winapi mfc

我正在使用后台工作线程(通过_beginthreadex创建)和UI线程在MFC中编写应用程序。从UI线程单击一个按钮以开始和结束工作线程。如果m_threadRunning标志为false,则启动后台线程,如果为真,则停止后台线程。我停止线程的方法是将m_threadRunning标志设置为false并调用WaitForSingleObject让后台线程完成它正在做的事情。

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

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

我已经提出了一些解决方案,但我不喜欢它们中的任何一种,并且宁愿了解导致问题的原因,因为我在MFC中编写此目标的目的是学习和理解MFC。以下是解决问题的方法:

  1. 我已经在我的工作线程中以大约60个样本/秒调用Sleep()。将此值更改为较低的值,如30个样本/秒,大部分时间都可以解决问题。
  2. 我在工作线程中轮询m_threadRunning以检查线程是否应该终止。如果我在对屏幕进行采样后但在更新编辑控件之前对其进行轮询,则可以在大多数时间解决问题。
  3. 我在调用WaitForSingleObject时执行5秒超时,并在无法等待时调用TerminateThread手动终止该线程,这一直解决了问题。这是我现在的解决方案。
  4. 以下是相关的代码位(我锁定任何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;
    }
    

    非常感谢您提供的任何帮助!

1 个答案:

答案 0 :(得分:3)

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

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

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

可以使用相同的技术来通知主线程工作线程已完成,这样就可以避免WaitForSingleObject。