2013-08-18 94 views
0

我有一個線程,做冗長的處理。在我等待線程完成時,我啓動了另一個「顯示進度」線程,它簡單地來回切換位圖以顯示程序正在處理數據。令我驚訝的是,這個方法根本行不通。我的'顯示進度線程'不運行時,它計數

我的'顯示progerss'線程在主要活動開始時停止更新(=正在運行),並且在活動結束時它開始更新。這幾乎是我想要的反對!我是否應該期待這種行爲,因爲WaitForSingleOBject大多數時間處於等待狀態並短暫醒來?

// This is the main thread that does the actual work 
CWinThread* thread = AfxBeginThread(threadDoWork, this, THREAD_PRIORITY_LOWEST, 0, CREATE_SUSPENDED); 
     thread->m_bAutoDelete = FALSE; 
     thread->ResumeThread(); 

// before I start to wait on the above thread, I start this thread which will toggle image to show application is processing 
AfxBeginThread(ProgressUpdateThread, &thread_struct_param, THREAD_PRIORITY_NORMAL, 0); 

// wait for the main thread now. 
DWORD dwWaitResult = WaitForSingleObject(thread->m_hThread, INFINITE); 

DWORD exitCode; 
::GetExitCodeThread(thread->m_hThread, &exitCode); 
delete thread; 

// This thread toggles image to show activity 
UINT ProgressUpdateThread(LPVOID param) 
{ 
    CEvent * exitEvent = ((mystruct *)param)->exitEvent; 
    MyView *view ((mystruct *)param)->view; 

    int picture = 0; 

    do 
    { 
     waitResult = WaitForSingleObject(exitEvent->m_hObject, 100); 

     if (waitResult == WAIT_TIMEOUT) 
     { 

      picture = toggle ? 1: 0; 

      // invert 
      toggle = !toggle; 

      View->Notify(UPDATE_IMAGE, picture); 
     } 
     else if (waitResult == WAIT_OBJECT_0) 
     { 
      return TRUE; 
     } 
    } 
    while(1); 
} 

在我的解決方案的另一個考慮是,我想不觸及實際「DoWork的」線程代碼這也是爲什麼我使用單獨的線程來更新GUI。我可以使這種方法起作用嗎?更新可靠GUI的唯一方法是從實際的'DoWork線程本身更新它?

我想澄清一下,如果應用程序空閒,我的'顯示進度'線程完成這項工作,但如果啓動工作線程操作(以較低的線程優先級),則更新GUI線程將停止運行並恢復只有當工人結束時。

我使用Windows 7

+1

通常,任何時候進度線程都無法更新UI,這是因爲所述UI不是通過消息循環泵送消息。自從我在MFC中完成線程工作以來,它一直是*永遠*,但是必須有樣本來說明如何在某個地方正確地完成這個任務。也許['MsgWaitForMultipleObjects()'](http://msdn.microsoft.com/en-us/library/windows/desktop/ms684242(v = vs.85).aspx)(仔細閱讀文檔*,其中的a使用起來有點棘手),一個適當管理的味精泵將做你想做的。這聽起來像你的主線程忙於處理一些東西,並沒有爲其循環服務 – WhozCraig

+0

實際上,在這種情況下,我直接從update-gui線程更新GUI。我知道有這樣的優點和缺點,但只要一個線程更新gui,它應該沒問題。 – zar

+1

它應該工作。如果你是主線程的確在服務一個msg-loop,那麼其他東西必須丟失。 – WhozCraig

回答

2

你的設計是完全錯誤的和過於複雜的對您嘗試的東西。嘗試更多的東西這樣簡單的解決辦法:

bool toggle = false; 

VOID CALLBACK updateProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) 
{ 
    int picture = toggle ? 1: 0; 
    toggle = !toggle; 
    View->Notify(UPDATE_IMAGE, picture); 
} 

CWinThread* thread = AfxBeginThread(threadDoWork, this, THREAD_PRIORITY_LOWEST, 0, CREATE_SUSPENDED); 
thread->m_bAutoDelete = FALSE; 
thread->ResumeThread(); 

UINT_PTR updateTimer = SetTimer(NULL, 0, 100, updateProc); 

do 
{ 
    DWORD dwWaitResult = MsgWaitForMultipleObjects(1, &(thread->m_hThread), FALSE, INFINITE, QS_ALLINPUT); 
    if (dwWaitResult == WAIT_OBJECT_0) 
     break; 

    if (dwWaitResult == (WAIT_OBJECT_0+1)) 
    { 
     MSG msg; 
     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
     { 
      TranslateMessage(&msg); 
      DispatchMessage(&msg); 
     } 
    } 
} 
while (true); 

KillTimer(NULL, updateTimer); 

DWORD exitCode; 
::GetExitCodeThread(thread->m_hThread, &exitCode); 
delete thread; 

如果你不希望使用一個獨立的程序定時器,可以調整的SetTimer()參數把它張貼WM_TIMER消息您可以選擇一個HWND,然後根據需要在該窗口的消息過程中執行UI更新。不過,您仍然需要消息循環來抽取定時器消息。

另一種方法是根本不做任何等待。一旦你開始工作線程,繼續其他事情,並讓工作線程完成它的工作時通知主UI線程。

+0

我同意你對OP的評論,但恐怕我沒有完全解決你的問題。我確實做了gui更新,我會將我自己的答案作爲建議的解決方案發布。 – zar

0

Remy Lebeau正確指出我的主GUI線程實際上正在工作線程中等待。現在,因爲我的worker gui-update線程(顯然)調用了gui函數,它在主GUI線程上又被阻塞了。如果主GUI線程處於等待或阻塞狀態,我意識到即使從第三個線程返回SetWindowText()也會使該線程處於等待狀態。

我不喜歡使用PeekandPump()機制,我認爲這是一個糟糕的設計氣味。它最初用於早期的Windows(我認爲在win95之前),它不是真正的多任務處理。據我所知,這不應該現在使用。

我的解決方案是將我在OP中發佈的整個代碼放在一個新線程中。所以我的按鈕點擊gui創建這個威脅並立即返回。這個主工作線程現在可以在其他線程上等待,並且我的GUI不會被阻塞。完成後,它會向父窗口發送消息以通知它。當應用程序在另一個線程中處理時,位圖現在使用單獨的gui-update線程完美更改。

+0

你是對的,最好的解決方案是將冗長的操作放入工作者線程中,並且根本不阻塞主UI線程。但是您不應該使用工作線程直接更新UI。這是主UI線程和主UI線程的責任。工作線程可以發佈到主UI線程來告訴它要更新什麼,但主UI線程需要是實際執行它們的線程。而創建一個單獨的線程來更新你的位圖是矯枉過正和不必要的。改爲使用簡單的計時器,或改用動畫GIF/AVI控件。 –

+0

你是對的,理想情況下,主要的GUI線程應該進行更新。此外,在多線程應用程序的完美世界中,來自進程工作線程的任何通知也應該是PostMessage(),並且絕不會發送SendMessage(),但實際上可以接受某些包含的內容。例如,我有一個觀察者模式,它在真正的多線程環境中確實不起作用。但幸好SendMessage()就像調用視圖一樣,在將GUI與業務邏輯分開時做得很好。 – zar

+0

我的模型確實向實際更新的視圖發送消息。因此,雖然更新是在視圖中完成的,但它由工作線程擁有(或者由其觸發),主GUI線程執行實際更新。唯一的縮小規模是該工作線程正在等待主GUI線程執行實際更新,而不是返回,這就是我的意思,它包含了一點 - 這不需要太多時間,而且它更適合與處理線。 – zar

相關問題