0

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

我的“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。

4

2 回答 2

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 线程。

于 2013-08-18T08:32:58.883 回答
0

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

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

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

于 2013-08-18T17:58:04.773 回答