多线程程序退出总是出错!请高手看看
一个简单的对话框程序,新建了一个线程刷新界面数据。但是在主对话框程序退出时,刷新数据线程总是退出不了。以下是关键部分代码,请
大家帮我看看。谢谢了!
//1、在窗口初始化处BOOL CTest2Dlg::OnInitDialog()新建实时数据显示线程
m_pDisplayThread = NULL;
m_bIsDisplay = true;
m_pDisplayThread=AfxBeginThread(DisplayThread,(LPVOID)this);//开始实时数据线程
//2、实时数据显示线程
UINT CTest2Dlg::DisplayThread(LPVOID pParam)
{
CTest2Dlg *pDlg=(CTest2Dlg *)pParam;
CString str;
int i=0;
while (pDlg->m_bIsDisplay)
{
Sleep(1000);
TRACE("IN DISPLAY THREAD ..... \n");
i++;
str.Format("%d", i+1000);//幅值
pDlg->GetDlgItem(IDC_EDIT1)->SetWindowText(str);
str.Format("%d", i+2000);//幅值
pDlg->GetDlgItem(IDC_EDIT2)->SetWindowText(str);
str.Format("%d", i+3000);//幅值
pDlg->GetDlgItem(IDC_EDIT3)->SetWindowText(str);
}
TRACE("display end \n");
return 0;
}
//程序退出时 WaitForSingleObject总是不能返回!
void CTest2Dlg::OnDestroy()
{
CDialog::OnDestroy();
// TODO: Add your message handler code here
TRACE("void CTest2Dlg::OnDestroy() \n");
m_bIsDisplay = false;
TRACE("Strar waiting m_pDisplayThread->m_hThread .............\n");
WaitForSingleObject(m_pDisplayThread->m_hThread,INFINITE);//线程结束不了!!!
TRACE(_T("end m_pDisplayThread \n"));
}
作者: xiaogang2000 发布时间: 2009-07-19
作者: biweilun 发布时间: 2009-07-19
作者: kumbayaco 发布时间: 2009-07-19
请高手继续帮忙
作者: xiaogang2000 发布时间: 2009-07-19
m_pDisplayThread->SetThreadPriority(THREAD_PRIORITY_ABOVE_NORMAL);
试试;
2、用全局变量进行线程同步,再 WaitForSingleObject(m_hThread,INFINITE);的方法确实不灵,你只能提供一个停止线程运行的按钮,如果用户退出时线程仍在运行,则提示用户先停止线程再退出程序。
3、用EVENT可以实现你的功能。用两个事件对象:一个用于告知工作线程退出,工作线程运行时定期检测该事件是否为有信号状态; 另一个为工作线程返回前设置为有信号状态,以告知主线程它已返回,供 WaitForSingleObject() 等待。
作者: gg606 发布时间: 2009-07-19
gg606
我试试你的方法
作者: xiaogang2000 发布时间: 2009-07-19
作者: pady_pady 发布时间: 2009-07-19
试试看
作者: wenh7788 发布时间: 2009-07-19
m_pDisplayThread->->m_bAutoDelete = FALSE; //保证线程退出码在外能被检查到。
m_pDisplayThread->ResumeThread();
试试这个
作者: byxdaz 发布时间: 2009-07-19
while (pDlg->m_bIsDisplay) { TRACE("IN DISPLAY THREAD ..... \n"); i++; str.Format("%d", i+1000);//幅值 pDlg->GetDlgItem(IDC_EDIT1)->SetWindowText(str); str.Format("%d", i+2000);//幅值 pDlg->GetDlgItem(IDC_EDIT2)->SetWindowText(str); str.Format("%d", i+3000);//幅值 pDlg->GetDlgItem(IDC_EDIT3)->SetWindowText(str); [color=#FF00FF]Sleep(1000); [/color] }
这样就可以了,不然死锁了
作者: pady_pady 发布时间: 2009-07-19
volatile BOOL m_bIsDisplay
作者: schlafenhamster 发布时间: 2009-07-19
{
CDialog::OnDestroy();
// TODO: Add your message handler code here
TRACE("void CTest2Dlg::OnDestroy() \n");
m_bIsDisplay = false;
TRACE("Strar waiting m_pDisplayThread->m_hThread .............\n");
WaitForSingleObject(m_pDisplayThread->m_hThread,INFINITE);//线程结束不了!!!
TRACE(_T("end m_pDisplayThread \n"));
}
将红色这段放函数最后试试。另外你这两个线程都对主界面有操作,有没进行同步处理,不安全。可以看看我的博文《利用自定义消息在VC内进行线程间通讯》http://blog.csdn.net/WooSir/archive/2009/07/15/4351977.aspx
作者: WooSir 发布时间: 2009-07-19
void CTest2Dlg::OnDestroy()
{
CDialog::OnDestroy();
// TODO: Add your message handler code here
TRACE("void CTest2Dlg::OnDestroy() \n");
m_bIsDisplay = false;
TRACE("Strar waiting m_pDisplayThread->m_hThread .............\n");
WaitForSingleObject(m_pDisplayThread->m_hThread,INFINITE);//线程结束不了!!!
TRACE(_T("end m_pDisplayThread \n"));
}
将红色这段放函数最后试试。另外你这两个线程都对主界面有操作,有没进行同步处理,不安全。可以看看我的博文《利用自定义消息在VC内进行线程间通讯》http://blog.csdn.net/WooSir/archive/2009/07/15/4351977.aspx
同意,退出操作一般都放在之前进行。
或者试试看m_pDisplayThread->PostThreadMessage(WM_QUIT,0,0);
作者: Fireway2008 发布时间: 2009-07-19
将Sleep(1000)
放在循环最后就可以正常。
非常不理解。
还有,上面很多朋友给出的方法好像都不好使,请大家帮忙确认下。
谢谢大家
作者: xiaogang2000 发布时间: 2009-07-24
作者: caitian6 发布时间: 2009-07-24
作者: xiaogang2000 发布时间: 2009-07-24
作者: caitian6 发布时间: 2009-07-24
好人啊!
作者: xiaogang2000 发布时间: 2009-07-24
CDialog::OnDestroy()是应该放到最后去,否则窗口都销毁了,你线程中的那些操作窗口的语句都会出错。至于你把sleep放在后面可以正常退出,其实是因为你的OnDestroy函数调用时,你的线程多半在Sleep,然后while循环会直接退出而没有运行窗口操作函数,所以程序不会出错。
作者: ysok 发布时间: 2009-07-25
作者: bdzwj 发布时间: 2009-07-25
作者: sanguomi 发布时间: 2009-07-25
其中显示线程的Sleep放在前面。
做了如下修改:
1、在m_bIsDisplay = false;增加2秒的延时
2、CDialog::OnDestory()应该放到最后
还是死在WaitForSingleObject这句上!!!!怎么回事???
//程序退出时 WaitForSingleObject总是不能返回!
void CTest2Dlg::OnDestroy()
{
// TODO: Add your message handler code here
TRACE("void CTest2Dlg::OnDestroy() \n");
m_bIsDisplay = false;
Sleep(2000);
TRACE("Strar waiting m_pDisplayThread->m_hThread .............\n");
WaitForSingleObject(m_pDisplayThread->m_hThread,INFINITE);//线程结束不了!!!
TRACE(_T("end m_pDisplayThread \n"));
CDialog::OnDestroy();
}
请大家亲自动手试试,谢谢了!
作者: xiaogang2000 发布时间: 2009-07-28
作者: icesnowjank 发布时间: 2009-07-28
void CTest2Dlg::OnDestroy()
{
// TODO: Add your message handler code here
TRACE("void CTest2Dlg::OnDestroy() \n");
m_bIsDisplay = false;
TRACE("Strar waiting m_pDisplayThread->m_hThread .............\n");
WaitForSingleObject(m_pDisplayThread->m_hThread,INFINITE);//线程结束不了!!!
TRACE(_T("end m_pDisplayThread \n"));
CDialog::OnDestroy();
}
作者: WooSir 发布时间: 2009-07-28
作者: dronly 发布时间: 2009-07-28
多线程程序退出总是出错!请高手看看
一个简单的对话框程序,新建了一个线程刷新界面数据。但是在主对话框程序退出时,刷新数据线程总是退出不了。以下是关键部分代码,请
大家帮我看看。谢谢了!
//1、在窗口初始化处BOOL CTest2Dlg::OnInitDialog()新建实时数据显示线程
m_pDisplayThread = NULL;
m_bIsDisplay = true;
m_pDisplayThread=AfxBeginThread(DisplayThread,(LPVOID)this);//开始实时数据线程
//2、实时数据显示线程
UINT CTest2Dlg::DisplayThread(LPVOID pParam)
{
CTest2Dlg *pDlg=(CTest2Dlg *)pParam;
CString str;
int i=0;
while (pDlg->m_bIsDisplay)
{
Sleep(1000);
TRACE("IN DISPLAY THREAD ..... \n");
i++;
str.Format("%d", i+1000);//幅值
pDlg->GetDlgItem(IDC_EDIT1)->SetWindowText(str);
str.Format("%d", i+2000);//幅值
pDlg->GetDlgItem(IDC_EDIT2)->SetWindowText(str);
str.Format("%d", i+3000);//幅值
pDlg->GetDlgItem(IDC_EDIT3)->SetWindowText(str);
}
TRACE("display end \n");
return 0;
}
//程序退出时 WaitForSingleObject总是不能返回!
void CTest2Dlg::OnDestroy()
{
CDialog::OnDestroy();
// TODO: Add your message handler code here
TRACE("void CTest2Dlg::OnDestroy() \n");
m_bIsDisplay = false;
TRACE("Strar waiting m_pDisplayThread->m_hThread .............\n");
WaitForSingleObject(m_pDisplayThread->m_hThread,INFINITE);//线程结束不了!!!
TRACE(_T("end m_pDisplayThread \n"));
}
1. 问题的重点是 多线程访问同一数据。无锁无原子操作。
2. 产生的原因是 线程2中将有可能永远使用寄存器中的copy数据。即使你改变了原数据。在线程2也可能是不知道的。
3. 解决办法 加锁, 或者加原子操作, 指定volatile关键字。color=#FF0000]指定volatile关键字将会关闭编译器对此数据访问的优化。每次读取原数据。[/color] 这样声明 volatile bool m_bIsDisplay;
4. 楼上有位说对了。没人理睬。
5. 分应该给我。您说呢。
作者: rendao0563 发布时间: 2009-07-28
解决办法,请参看:
http://topic.csdn.net/u/20090507/15/4df84be3-9823-4599-967d-51881a85377f.html
作者: Lin 发布时间: 2009-07-28
要放到最后的
作者: wsj239237 发布时间: 2009-07-29
作者: icesnowjank 发布时间: 2009-07-29
但是,还是存在退不出的情况。(10次可能有1次不能退出)
请高人们继续讨论
作者: xiaogang2000 发布时间: 2009-07-31
2个线程共用一个变量,加volatile试试:
volatile BOOL m_bIsDisplay
这样可以保证m_bIsDisplay每一次都从地址中取数据,而不是用一个临时拷贝,正解
作者: visualthinking 发布时间: 2009-07-31
不要用会潜在发消息的方法,要直接用SendMessageTimeout代替。考虑到参数可能带有指针,主线程退出时应当向一个你定义的标志整数里写退出值(值当然由你定)。而线程函数用循环的SendMessageTimeout向控制窗口发消息。如果返回成功则退出循环(这里不是消息循环)(说明消息发送成功了);如果超时就检查该标志整数是否是退出状态。如果否,说明控制窗口只是响应慢,但可以处理,此时应当重试发送,如果该标志整数是退出状态,说明你的控制窗口正在等待线程结束呢!还不退出循环干什么?!(不然就会死锁)。
// 此函数从工作者线程发至控制窗口:
LRESULT SendNonLockedMessage(HWND hwnd, UINT uMsg, WPARAM wPrm, LPARAM lPrm, volatile size_t * pThrExitChk)
{
LRESULT lRet = 0;
while(!SendMessageTimeout(hwnd, uMsg, wPrm, lPrm, SMTO_BLOCK, 50, &lRet))
{
if(*pThrExitChk == ID_YOUR_EXIT_CODE)
break;
}
return(lRet);
}
这函数还可以改更好,我没有时间改,思路告诉你了
作者: unituniverse2 发布时间: 2009-08-01
作者: unituniverse2 发布时间: 2009-08-01
作者: unituniverse2 发布时间: 2009-08-01
if(m_bMainThreadAlive)//关闭主线程
{
do
{
SetEvent(m_hMainShutdownEvent);
} while (m_bMainThreadAlive);
}
至于线程那边该怎么做,可以查看MSDN自己考虑下
作者: lymxuanlin 发布时间: 2009-08-01
在线程中操作界面,就不推荐。
推荐的做法是向相应的窗口的句柄,PostMessage消息。
作者: caitian6 发布时间: 2009-08-03
作者: unituniverse2 发布时间: 2009-08-03
作者: reneeland 发布时间: 2009-08-03
你在程序里面加上Sleep()只是让你的线程大部分的时间处于Idle状态。
Sleep(1000)放在循环前面,大部分的情况是OnDesotry等待线程结束时,线程在Sleep,线程完成Sleep后立刻开始调用SetWindowText,立刻会死锁,因为OnDesotry正在等线程结束。
Sleep(1000)放在循环后面,大部分情况是OnDestory等待线程结束时,线程在Sleep,线程Sleep完了后,立刻检查pDlg->m_bIsDisplay, 一定是False(OnDestory函数设的),这时循环退出,正常结束。
结论是Sleep(1000)放在前面大大增加死锁的机会,放在后面大大减小死锁的机会,但是还是不可避免。
作者: formula1kimi 发布时间: 2009-08-03
作者: unituniverse2 发布时间: 2009-08-04
是了值的,
在你sleep(1000),OnDestroy 窗口消亡了,
m_bIsDisplay也就不是false,
线程退不出。最好放到Onclose()里
作者: king_query 发布时间: 2009-08-05
经过实践,
MsgWaitForMultipleObjects
方法最为有效。但是偶尔还是会出现问题。
大家都很热心,请动手自己试试,相信会对自己有帮助的。
再次感谢这么多人的帮忙!!!
作者: xiaogang2000 发布时间: 2009-08-07
首先得明白你的案例死锁的原因。显然是工作者线程向窗口发WM_SETTEXT消息时窗口正在处理WM_DESTROY,情形如38楼所说那样。MsgWaitForMultipleObjects这里帮不上你的忙。你可以查下它的说明。首先你不可能指定“全部”标志,难道要指定“其中之一”标识吗?肯定也不行!如果有消息发来,就可能会出现OnDestroy处理完后,工作者线程还没返回的情况。这显然违背了你的设计初衷。为什么不用SendMessageTimeout的循环发送方法?或者不发消息而是写入某个共享值,然后窗口用Timer事件去取该值以获得你要的幅值。涉及到共享值的都得用Interlocked函数写入(而不要直接用赋值代码),以同步各处理器的高速缓存。共享值都得声明volatile。用WaitForObject等待事件、互斥体、信号量都靠不住,不象等线程和进程句柄那样。
作者: unituniverse2 发布时间: 2009-08-09
作者: xinxingxiangying 发布时间: 2009-08-10
作者: unituniverse2 发布时间: 2009-08-10
作者: hello101105 发布时间: 2009-08-12
樓主的線程有問題
1線程里面不要直接刷新界面數據,容易出現問題。
2format函數在多執行緒中有時會掛掉。
3你線程處理的刷新如果有點慢的話,當你剛剛要刷新數據時,主窗體被釋放了,那么在主窗體釋放后,還會執行還沒刷新完的部分,而這時那些數據對象全部為空了,就出錯了。建議在釋放主窗體前,先退出線程,在判斷線程完全退出后再進行主窗體的釋放,
作者: Dolphin_001 发布时间: 2009-08-15
SetWindowText会向你的UI主线程发SendMessage消息,你的OnDestory是主线程正在处理的消息。当OnDestory被主线程调用后,你的工作线程就会停在SetWindowText调用上,等你的主线程去处理他的消息。而你的主线程却在等你的工作线程结束。结果谁也等不到谁。死锁了。
你在程序里面加上Sleep()只是让你的线程大部分的时间处于Idle状态。
Sleep(1000)放在循环前面,大部分的情况是OnDesotry等待线程结束时,线程在Sleep,线程完成Sleep后立刻开始调用SetWindowText,立刻会死锁,因为OnDesotry正在等线程结束。
Sleep(1000)放在循环后面,大部分情况是OnDestory等待线程结束时,线程在Sleep,线程Sleep完了后,立刻检查pDlg->m_bIsDisplay, 一定是False(OnDestory函数设的),这时循环退出,正常结束。
结论是Sleep(1000)放在前面大大增加死锁的机会,放在后面大大减小死锁的机会,但是还是不可避免。
赞同这个!
作者: orientalcool 发布时间: 2010-01-11
不推荐用事件。用waitforxxxxxobject获取事件、互斥体、信号量,api会申请内存,如果申请失败就会使本次wait操作失败(不要讲什么现在内存多么大的话...系统只会给你几兆用来放内核对象)。而使用锁就不会有该问题(但是会导致多处理器串列处理而降低性能。不过即使这样也比调用Waitobject等待要快20到100倍)。PostMessage不会导致死锁,但有两个问题。一个是PostMe?-
分应该给你!
作者: orientalcool 发布时间: 2010-01-11
后面加一句
CloseHandle(m_pDisplayThread->m_hThread)
2. CDialog::Destroy() 放到最后
作者: wangli820 发布时间: 2010-03-13
引用 38 楼 formula1kimi 的回复:SetWindowText会向你的UI主线程发SendMessage消息,你的OnDestory是主线程正在处理的消息。当OnDestory被主线程调用后,你的工作线程就会停在SetWindowText调用上,等你的主线程去处理他的消息。而你的主线程却在等你的工作线程结束。结果谁也等不到谁。死锁了。你在程序里面加上Sleep()只是让你的线程大部分……
作者: yekoufeng 发布时间: 2010-03-15
建议解决办法:主线程在设置标志通知线程结束之后立即返回,线程在退出前给窗口发送一个自定义消息,主线程响应这个消息关闭窗口(无须等待线程结束)。
作者: w52770567 发布时间: 2011-01-11