2013-08-27 133 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。 –