在 MFC 中同时挂起 2 个线程的问题!
Posted
技术标签:
【中文标题】在 MFC 中同时挂起 2 个线程的问题!【英文标题】:Problem in suspending 2 threads at the same time in MFC! 【发布时间】:2010-04-29 11:07:42 【问题描述】:我正在学习线程和多线程..所以我刚刚创建了一个小应用程序,我将在其中更新 进度条和使用线程的静态文本。我从用户那里得到两个输入,开始值和结束值 循环应该旋转多长时间。我的应用程序中有 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;
线程1函数
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;
线程2
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;
线程2函数
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);*/
【问题讨论】:
您说问题在于线程没有正确挂起/恢复,但您没有向我们展示处理“暂停”和“恢复”按钮点击的代码。 除此之外,我知道您这样做是为了学习线程,但这似乎是一项更适合计时器的任务。 另外,你说它“不起作用”。它以什么方式不起作用?它会崩溃吗?停止响应?就继续更新显示? 它只是冻结了无限时间!说没有响应 【参考方案1】:我想我应该把cmets中的一些讨论总结成一个答案。
在 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 ms 的间隔发生,另一个以 150 ms 的间隔发生),那么您可以使用不同的 ID 调用两次 SetTimer
。要暂停操作,请致电KillTimer
。要恢复它,请再次致电SetTimer
。
【讨论】:
TBH 如果您知道自己在做什么,那么从不同的线程进行更新是没有危险的。我一直这样做,没有问题:)【参考方案2】:多线程和消息队列是一个相当复杂的游戏。当您从 ThreadA SendMessage 到同一个线程时,它只会调用消息处理程序。如果您从 ThreadA 到另一个线程 (ThreadB) 执行此操作,那么它会变得更加复杂。然后,ThreadA 向 ThreadB 的消息队列发送一条消息,并等待一个信号表明 ThreadB 已完成对消息的处理并发送了返回值。这引发了一个即时问题。如果 ThreadB 没有发送消息,那么您将陷入死锁,因为 ThreadB 中的消息永远不会被“分派”。这也引发了一个更大的问题。如果 ThreadB 的消息需要将消息发送到在 ThreadA 中创建的控件,那么您将遇到巨大的体系结构问题。由于 ThreadA 当前处于挂起状态,等待 ThreadB 返回,而 ThreadB 处于挂起状态,等待 ThreadA 返回。什么都不会发生......他们都将被暂停。
真的就是这样。只要您牢记这些问题,它就很容易。即,尽管其他人说了什么,这绝对是可能的。
一般来说,尽管您的线程非常没有意义,因为您会立即向主线程发送消息以进行一些处理。为什么要首先启动线程。您最好不要打扰,因为线程只会坐在那里等待主线程返回。
当你挂起第一个线程时,为什么你仍然“WaitForSingleObject”?为什么不暂停他们两个。
不过,您并没有提供足够的信息来说明您正在做什么,从而无法准确说明发生了什么。例如,当您单击暂停时会发生什么?
【讨论】:
【参考方案3】:当多个线程与 GUI 交互时,Windows 将无法正常运行。您需要重新组织您的程序,以免发生这种情况。
【讨论】:
非常感谢您的回复..我现在有 2 个关于您的回答的问题 1.我们可以使用 2 个或更多线程来执行操作,例如读取文件或检查线程所在文件中的内容与GUI无关? 2.请告诉我另一种方法可以做到吗? @kiddo,是的,从后台线程操作非 GUI“事物”很好。另一种方法是使用PostMessage
而不是SendMessage
。这会导致将消息添加到主线程的消息队列以在那里处理,而不是由调用线程立即处理——尽管正如我在上面的评论中指出的那样,使用计时器可能会更好地处理这样的事情。
@kiddo 如果您使用PostMessage
,您还需要将Sleep
调用移出消息处理函数并进入线程函数,以免使主线程进入睡眠状态: )
好的,我不确定如何使用定时器。它就像在特定时间后使用 SetTimer 调用线程一样,你能给我举个例子吗
我还尝试用 postMessage 替换 SendMessage ..事情是,一个线程将一次运行..我猜这不是实际的线程以上是关于在 MFC 中同时挂起 2 个线程的问题!的主要内容,如果未能解决你的问题,请参考以下文章