我有一个执行冗长处理的线程。当我等待线程完成时,我启动了另一个'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。
答案 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线程完美地改变了位图。