我的“显示进度线程”在计数时不会运行

时间:2013-08-18 07:30:11

标签: c++ multithreading winapi model-view-controller mfc

我有一个执行冗长处理的线程。当我等待线程完成时,我启动了另一个'show progress'线程,它只是来回切换一个位图来显示程序正在处理数据。令我惊讶的是,这种方法根本不起作用。

我的'show 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。

2 个答案:

答案 0 :(得分: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线程。

答案 1 :(得分:0)

Remy Lebeau正确地指出我的主GUI线程实际上正在等待工作线程。现在,因为我的工作者gui-update线程(显然)调用了gui函数,所以它在主GUI线程上被阻止了。我意识到,如果主GUI线程处于等待或阻塞状态,即使是第三个线程的SetWindowText()也会将该线程置于等待状态。

我不喜欢使用PeekandPump()机制,我认为这是一个糟糕的设计气味。它最初用于早期的窗口(我认为在win95之前)并不是真正的多任务处理。据我所知,现在不应该使用它。

我的解决方案是将我在OP中发布的整个代码放在一个新线程中。所以我在gui中点击按钮会创建此威胁并立即返回。这个主工作线程现在可以在其他线程上等待,我的GUI永远不会阻塞。完成后,它会向父窗口发送一条消息以通知它。当应用程序在另一个线程中处理时,现在使用单独的gui-update线程完美地改变了位图。