我正在学习线程和多线程。所以我刚刚创建了一个小应用程序,我将在其中进行更新 进度条和静态文本使用线程。我从用户获得两个输入,开始和结束值 循环应旋转多长时间。我的应用程序中有2个线程。
Thread1-更新进度条(根据循环)静态文本,它将显示计数(循环计数)。 Thread2 - 更新另一个静态文本,它只会显示一个名称
基本上,如果用户单击“开始”,则进度条会逐步上升,同时文件计数和名称将平行显示。 还有另一种操作,如果用户点击暂停,它(线程)必须暂停,直到用户点击恢复。 问题是,上面的两个线程都不起作用(不会暂停和恢复)但是适用于单个线程。 请检查代码以获得一个想法并回复我可以做的事情!
按钮点击开始
void CThreadingEx3Dlg::OnBnClickedStart()
{
m_ProgressBar.SetRange(start,end);
myThread1 = AfxBeginThread((AFX_THREADPROC)MyThreadFunction1,this);
myThread2 = AfxBeginThread((AFX_THREADPROC)MyThreadFunction2,this);
}
线程1
UINT MyThreadFunction1(LPARAM lparam)
{
CThreadingEx3Dlg* pthis = (CThreadingEx3Dlg*)lparam;
for(int intvalue =pthis->start;intvalue<=pthis->end; ++intvalue)
{
pthis->SendMessage(WM_MY_THREAD_MESSAGE1,intvalue);
}
return 0;
}
thread1函数
LRESULT CThreadingEx3Dlg::OnThreadMessage1(WPARAM wparam,LPARAM lparam)
{
int nProgress= (int)wparam;
m_ProgressBar.SetPos(nProgress);
CString strStatus;
strStatus.Format(L"Thread1:Processing item: %d", nProgress);
m_Static.SetWindowText(strStatus);
Sleep(100);
return 0;
}
thread2
UINT MyThreadFunction2(LPARAM lparam)
{
CThreadingEx3Dlg* pthis = (CThreadingEx3Dlg*)lparam;
for(int i =pthis->start;i<=pthis->end;i++)
{
pthis->SendMessage(WM_MY_THREAD_MESSAGE2,i);
}
return 0;
}
thread2函数
LRESULT CThreadingEx3Dlg::OnThreadMessage2(WPARAM wparam,LPARAM lparam)
{
m_Static1.GetDlgItem(IDC_STATIC6);
m_Static1.SetWindowTextW(L"Thread2 Running");
Sleep(100);
m_Static1.SetWindowTextW(L"");
Sleep(100);
return TRUE;
}
void CThreadingEx3Dlg::OnBnClickedPause()
{
// TODO: Add your control notification handler code here
if(!m_Track)
{
m_Track = TRUE;
GetDlgItem(IDCANCEL)->SetWindowTextW(L"Resume");
myThread1->SuspendThread();
WaitForSingleObject(myThread1->m_hThread,INFINITE);
myThread2->SuspendThread();
m_Static.SetWindowTextW(L"Paused..");
}
else
{
m_Track = FALSE;
GetDlgItem(IDCANCEL)->SetWindowTextW(L"Pause");
myThread1->ResumeThread();
myThread2->ResumeThread();
/*myEventHandler.SetEvent();
WaitForSingleObject(myThread1->m_hThread,INFINITE);*/
}
}
答案 0 :(得分:4)
我认为我应该将评论中的一些讨论总结为一个答案。
在Windows编程中,您永远不应该尝试从后台线程操作GUI控件,因为这样做会导致程序死锁。这意味着只有主线程才能触及GUI的元素。 (从技术上讲,重要的是哪个线程创建了控件,但在后台线程中创建控件并不常见。)
此要求详见Joe Newcomer's article on worker threads(请参阅“工作线程和GUI II:不要触摸GUI”)。
您在线程程序中使用SendMessage
。这会导致调用目标控件的相应消息处理程序,但调用SendMessage
的线程中的。在您的情况下,这意味着后台线程运行消息处理程序,因此更新进度条和标签。
另一种方法是使用PostMessage
。这会将消息添加到队列中,以便由主线程的消息循环处理。当主线程运行时,它按照它们添加到队列的顺序处理消息,调用消息处理程序本身。由于主线程拥有窗口,因此更新控件是安全的。
您还应该注意SuspendThread
和ResumeThread
很难做到。您可能想阅读Joe Newcomer的文章this section,该文章描述了一些危险。
使用timer通常可以更好地完成这样的任务。这是一种在特定时间过后让操作系统通知您的程序的机制。您可以使用以下计时器实现此目的:
BEGIN_MESSAGE_MAP(CThreadingEx3Dlg, CDialog)
ON_WM_DESTROY()
ON_WM_TIMER()
END_MESSAGE_MAP()
void CThreadingEx3Dlg::OnTimer(UINT_PTR nTimerID)
{
static int progress = 0;
if (nTimerID == 1)
{
m_ProgressBar.SetPos(progress);
CString strStatus;
strStatus.Format(_T("Processing item: %d"), progress);
m_Static.SetWindowText(strStatus);
progress++;
if (progress > end) // If we've reached the end of the updates.
KillTimer(1);
}
}
BOOL CThreadingEx3Dlg::OnInitDialog()
{
// ... initialize controls, etc, as necessary.
SetTimer(1, 100, 0);
}
void CThreadingEx3Dlg::OnDestroy()
{
KillTimer(1);
}
如果您希望同时处理两个更新,则可以使用相同的计时器。如果它们需要在不同的时间发生(例如,一个间隔为100毫秒,另一个间隔为150毫秒),那么您可以使用不同的ID呼叫SetTimer
两次。要暂停操作,请致电KillTimer
。要恢复此操作,请再次致电SetTimer
。
答案 1 :(得分:1)
多线程和消息排队是一个非常复杂的游戏。当您将ThreadMessage从ThreadA发送到同一个线程时,它只是调用消息处理程序。如果从ThreadA到另一个线程(ThreadB),那么它会变得更复杂。然后,ThreadA将消息发送到ThreadB的消息队列,并等待一个信号,说明ThreadB已完成处理消息并发送了返回值。这引发了一个即时问题。如果ThreadB没有抽取消息,那么你就会遇到死锁,因为ThreadB中的消息永远不会被“分派”。这也引发了一个更大的问题。如果ThreadB的消息需要向ThreadA中创建的控件发送消息,那么您就会遇到大量的架构问题。由于ThreadA当前暂停等待ThreadB返回并且ThreadB被挂起,等待ThreadA返回。什么都不会发生......他们都会被暂停。
真的是这样的。只要你记住这些问题,它就很容易了。即尽管其他人所说的话,它绝对是可能的。
一般情况下,虽然你的线程是毫无意义的,因为你会立即向主线程发送一条消息来进行一些处理。为什么首先要打扰线程。你也可以不打扰,因为线程只会坐在那里等待主线程返回。
为什么在挂起第一个线程的时候“WaitForSingleObject”呢?为什么不暂停它们。
尽管如此,你并没有提供足够的信息来说明你正在做什么来说明发生了什么。单击暂停时会发生什么?例如?
答案 2 :(得分:0)
当多个线程与GUI交互时,Windows将无法正常运行。您需要重新组织您的程序,以免发生。