多线程程序退出总是出错!请高手看看

多线程程序退出总是出错!请高手看看

一个简单的对话框程序,新建了一个线程刷新界面数据。但是在主对话框程序退出时,刷新数据线程总是退出不了。以下是关键部分代码,请

大家帮我看看。谢谢了!

//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

1、在 WaitForSingleObject(m_pDisplayThread->m_hThread,INFINITE); 前加一句:
m_pDisplayThread->SetThreadPriority(THREAD_PRIORITY_ABOVE_NORMAL);
试试;
2、用全局变量进行线程同步,再 WaitForSingleObject(m_hThread,INFINITE);的方法确实不灵,你只能提供一个停止线程运行的按钮,如果用户退出时线程仍在运行,则提示用户先停止线程再退出程序。
3、用EVENT可以实现你的功能。用两个事件对象:一个用于告知工作线程退出,工作线程运行时定期检测该事件是否为有信号状态; 另一个为工作线程返回前设置为有信号状态,以告知主线程它已返回,供 WaitForSingleObject() 等待。

作者: gg606   发布时间: 2009-07-19

非常感谢  
gg606

我试试你的方法

作者: xiaogang2000   发布时间: 2009-07-19

Sleep(1000); 移到循环最后一句应该可以了,死锁问题

作者: pady_pady   发布时间: 2009-07-19

先选中项目清理解决方案然后重新生成在运行在退出还会报错吗??
试试看

作者: wenh7788   发布时间: 2009-07-19

m_pDisplayThread=AfxBeginThread(DisplayThread,(LPVOID)this,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED,NULL);//开始实时数据线程
m_pDisplayThread->->m_bAutoDelete = FALSE; //保证线程退出码在外能被检查到。
m_pDisplayThread->ResumeThread();

试试这个

作者: byxdaz   发布时间: 2009-07-19

C/C++ code

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

2个线程共用一个变量,加volatile试试:
volatile BOOL m_bIsDisplay

作者: schlafenhamster   发布时间: 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

作者: WooSir   发布时间: 2009-07-19

引用 11 楼 woosir 的回复:
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

你的线程一得到CUP时间,你就让他休息1000毫秒,他总共的得到的CUP时间就几个指令周期,当然小于1000毫秒,所以代码不往While循环的下面走

作者: caitian6   发布时间: 2009-07-24

sleep放在后面为什么又可以?

作者: xiaogang2000   发布时间: 2009-07-24

貌似没有解释对,高手继续

作者: caitian6   发布时间: 2009-07-24

感谢“caitian6”
好人啊!

作者: xiaogang2000   发布时间: 2009-07-24

线程没有任何锁,哪来的死锁啊,真是晕
CDialog::OnDestroy()是应该放到最后去,否则窗口都销毁了,你线程中的那些操作窗口的语句都会出错。至于你把sleep放在后面可以正常退出,其实是因为你的OnDestroy函数调用时,你的线程多半在Sleep,然后while循环会直接退出而没有运行窗口操作函数,所以程序不会出错。

作者: ysok   发布时间: 2009-07-25

简单看了一下代码,不用想其他的,80%以上就是运行次序问题, CDialog::OnDestory()应该放到最后。 或者不当调用基类,自己直接销毁窗口。

作者: bdzwj   发布时间: 2009-07-25

加个互斥体,

作者: sanguomi   发布时间: 2009-07-25

CDialog::OnDestory()应该放到最后也不行!!!
其中显示线程的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

用ACE或者是 boost.asio吧~~ 老掉牙的mfc开线程的方式不出错才怪咧~~~

作者: 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

引用楼主 xiaogang2000 的回复:
多线程程序退出总是出错!请高手看看

一个简单的对话框程序,新建了一个线程刷新界面数据。但是在主对话框程序退出时,刷新数据线程总是退出不了。以下是关键部分代码,请

大家帮我看看。谢谢了!

//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

CDialog::OnDestroy();
要放到最后的

作者: wsj239237   发布时间: 2009-07-29

直接把线程杀掉~~~~~~

作者: icesnowjank   发布时间: 2009-07-29

采用“Lin”的方案,很大程度上能够解决问题。

但是,还是存在退不出的情况。(10次可能有1次不能退出)

请高人们继续讨论

作者: xiaogang2000   发布时间: 2009-07-31

引用 10 楼 schlafenhamster 的回复:
2个线程共用一个变量,加volatile试试:
volatile BOOL m_bIsDisplay

这样可以保证m_bIsDisplay每一次都从地址中取数据,而不是用一个临时拷贝,正解

作者: visualthinking   发布时间: 2009-07-31

肯定死锁了,消息循环也是一个潜在的同步源,任意时刻一个消息泵只能处理最多一个线程的消息,其他线程的必须等待。楼上的很多方法其实不可靠。加volatile解决不了问题,在多处理器环境下回由于不同处理器的高速缓存内容不一致而失效。
不要用会潜在发消息的方法,要直接用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

SendNonLockedMessage的WPARAM/LPARAM参数传递指针不会引起访问问题

作者: unituniverse2   发布时间: 2009-08-01

然后在你的OnDestroy方法里,WaitForSingleObject前加上将标志整数赋值成ID_YOUR_EXIT_CODE的语句

作者: unituniverse2   发布时间: 2009-08-01

再加个Event对象就可以解决问题了
if(m_bMainThreadAlive)//关闭主线程
{
  do
  {
SetEvent(m_hMainShutdownEvent);
  } while (m_bMainThreadAlive);
}
至于线程那边该怎么做,可以查看MSDN自己考虑下

作者: lymxuanlin   发布时间: 2009-08-01

顶顶更健康,楼主该结贴啦。
在线程中操作界面,就不推荐。
推荐的做法是向相应的窗口的句柄,PostMessage消息。

作者: caitian6   发布时间: 2009-08-03

不推荐用事件。用waitforxxxxxobject获取事件、互斥体、信号量,api会申请内存,如果申请失败就会使本次wait操作失败(不要讲什么现在内存多么大的话...系统只会给你几兆用来放内核对象)。而使用锁就不会有该问题(但是会导致多处理器串列处理而降低性能。不过即使这样也比调用Waitobject等待要快20到100倍)。PostMessage不会导致死锁,但有两个问题。一个是PostMessage不象SendMessage那样插队,而是投递到队列末端的,如果窗口有大量消息等待处理以至于消息队列满时调用该函数,就会失败;另一个是,调用PostMessage总是先返回的,消息的处理发生在该函数返回后(具体多少,还不一定!)。因此不能传递指针(当消息处理时,现在的指针值都不知道指向什么去了)。

作者: unituniverse2   发布时间: 2009-08-03

关注一下!占位留名,呵呵~~~

作者: reneeland   发布时间: 2009-08-03

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)放在前面大大增加死锁的机会,放在后面大大减小死锁的机会,但是还是不可避免。

作者: formula1kimi   发布时间: 2009-08-03

LZ睡觉去了...

作者: unituniverse2   发布时间: 2009-08-04

m_bIsDisplay = false;
是了值的,
在你sleep(1000),OnDestroy 窗口消亡了,
m_bIsDisplay也就不是false,
线程退不出。最好放到Onclose()里

作者: king_query   发布时间: 2009-08-05

楼主没睡觉,出差去了。


经过实践,

MsgWaitForMultipleObjects

方法最为有效。但是偶尔还是会出现问题。

大家都很热心,请动手自己试试,相信会对自己有帮助的。

再次感谢这么多人的帮忙!!!

作者: xiaogang2000   发布时间: 2009-08-07

不是100%有效的方法,按时不会去用的...
首先得明白你的案例死锁的原因。显然是工作者线程向窗口发WM_SETTEXT消息时窗口正在处理WM_DESTROY,情形如38楼所说那样。MsgWaitForMultipleObjects这里帮不上你的忙。你可以查下它的说明。首先你不可能指定“全部”标志,难道要指定“其中之一”标识吗?肯定也不行!如果有消息发来,就可能会出现OnDestroy处理完后,工作者线程还没返回的情况。这显然违背了你的设计初衷。为什么不用SendMessageTimeout的循环发送方法?或者不发消息而是写入某个共享值,然后窗口用Timer事件去取该值以获得你要的幅值。涉及到共享值的都得用Interlocked函数写入(而不要直接用赋值代码),以同步各处理器的高速缓存。共享值都得声明volatile。用WaitForObject等待事件、互斥体、信号量都靠不住,不象等线程和进程句柄那样。

作者: unituniverse2   发布时间: 2009-08-09

线程,比较难的技术,尤其是数据共享,正在研究中……

作者: xinxingxiangying   发布时间: 2009-08-10

线程不吃透不行了。大概以后提高性能的方法只有两个:单线程的用非硅处理器(例如光处理器),或者多线程。例如显示处理器。可以研究下nvidia CUDA和Intel Parallel Studio。

作者: unituniverse2   发布时间: 2009-08-10

我也遇到了这问题,顶了,学习!

作者: hello101105   发布时间: 2009-08-12

不mfc不太熟,線程結束不是用m_pDisplayThread->teiminate或者Windows.TerminateThread么?
樓主的線程有問題
1線程里面不要直接刷新界面數據,容易出現問題。
2format函數在多執行緒中有時會掛掉。
3你線程處理的刷新如果有點慢的話,當你剛剛要刷新數據時,主窗體被釋放了,那么在主窗體釋放后,還會執行還沒刷新完的部分,而這時那些數據對象全部為空了,就出錯了。建議在釋放主窗體前,先退出線程,在判斷線程完全退出后再進行主窗體的釋放,

作者: Dolphin_001   发布时间: 2009-08-15

引用 38 楼 formula1kimi 的回复:
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

引用 36 楼 unituniverse2 的回复:
不推荐用事件。用waitforxxxxxobject获取事件、互斥体、信号量,api会申请内存,如果申请失败就会使本次wait操作失败(不要讲什么现在内存多么大的话...系统只会给你几兆用来放内核对象)。而使用锁就不会有该问题(但是会导致多处理器串列处理而降低性能。不过即使这样也比调用Waitobject等待要快20到100倍)。PostMessage不会导致死锁,但有两个问题。一个是PostMe?-


分应该给你!

作者: orientalcool   发布时间: 2010-01-11

1.m_pDisplayThread=AfxBeginThread(DisplayThread,(LPVOID)this);//开始实时数据线程 
后面加一句
 CloseHandle(m_pDisplayThread->m_hThread)
2. CDialog::Destroy() 放到最后

作者: wangli820   发布时间: 2010-03-13

引用 47 楼 orientalcool 的回复:
引用 38 楼 formula1kimi 的回复:SetWindowText会向你的UI主线程发SendMessage消息,你的OnDestory是主线程正在处理的消息。当OnDestory被主线程调用后,你的工作线程就会停在SetWindowText调用上,等你的主线程去处理他的消息。而你的主线程却在等你的工作线程结束。结果谁也等不到谁。死锁了。你在程序里面加上Sleep()只是让你的线程大部分……
很透彻,但是别的人也该给点分, 要不下次没人回答你问题了,哈

作者: yekoufeng   发布时间: 2010-03-15

线程互锁了,常见的原因是:线程中要访问主线程的窗口(或窗口中的控件),因为访问窗口实际上是通过SendMessage由窗口所属线程来处理的,而主线程此时在等待另一个由线程触发的事件(线程结束),而没有取消息,所以线程一直在等待主线程处理消息而不会结束,结果就是两个线程永远等待。
建议解决办法:主线程在设置标志通知线程结束之后立即返回,线程在退出前给窗口发送一个自定义消息,主线程响应这个消息关闭窗口(无须等待线程结束)。

作者: w52770567   发布时间: 2011-01-11