MFC中工作者线程调用状态栏,死掉原因

转载 2014年07月06日 12:01:46

这几天遇到问题,工作者线程调用窗口状态栏死掉,看到下面这篇博客,特转载留念。

出处:http://blog.csdn.net/caowei880123/article/details/5991962

官方帮助说明:http://support.microsoft.com/default.aspx?scid=kb;en-us;147578


最近在编写一个基于对话框的MFC程序,在程序中创建了一个子线程,想通过子线程获取和控制主窗口中的一个编辑框显示数据,经查资料现总结如下:

1.可以在创建子线程的时候,将窗口的句柄作为参数传递给子线程
2.可以通过自定义消息来给主线程来发送命令
3.通过HEVENT 事件内核对象来给主线程来发送信号,通知主线程刷新编辑框内容

注:在子线程中直接操作主窗体,要注意加锁,窗体不是线程安全的,建议采用2.通过自定义消息来给主线程来发送命令

另外,UpdateData不要在子线程中用会卡的! 以下是我搜集的MFC多线程应注意的事项,很乱但很有用:

--------------------------------------------------------------------------------MFC多线程编程注意事项 - --------------------------------------------------------------------------------

关于启动线程时传输窗口对象(指针?句柄?)的问题:

在选择菜单中的开始线程后: void cmainframe::onmenu_start() { ... afxbeginthread(mythread, this); ... }

线程函数如下:

uint mythread(lpvoid pparam) { cmainframe* pmainfrm = (cmainframe *)pparam; ... }

问题一: 这样的代码是不是有问题? (文档中说线程间不能直接传输mfc对象的指针,应该通过传输句柄实现)

 问题二: 这样使用开始好像没有问题,直接通过pmainfrm访问窗口中的view都正常。 但发现访问状态条时: pmainfrm->m_wndstatusbar.setpanetext(2, "test); 出现debug assertion failed!(在窗口线程中没有问题) 位置是wincore.cpp中的 assert((p = pmap->lookuppermanent(m_hwnd)) != null || (p = pmap->lookuptemporary(m_hwnd)) != null); 为什么访问view能正常,但访问状态条时不可以呢?

 问题三: 如果通过传输句柄实现,怎样做呢? 我用下面的代码执行时有问题: void cmainframe::onmenu_start() { ... hwnd hwnd = getsafehwnd(); afxbeginthread(mythread, hwnd); ... } uint mythread(lpvoid pparam) { cmainframe* pmainfrm = (cmainframe *)(cwnd::fromhandle((hwnd)pparam)); ... } 执行时通过线程中得到pmainfrm,访问其成员时不正常。

 网友:hewwatt大致原因解释如下:

1. mfc的大多数类不是线程安全的,cwnd及其消息路由是其中之最

 2. mfc界面类的大多数方法,最后都是通过sendmessage实现的,而消息处理的 过程中会引发其他消息的发送及处理。如果消息处理函数本身不是线程安全的 你从工作线程中调用这些方法迟早会同你界面线程的用户消息响应发生冲突

3. cxxxx::fromhandle会根据调用者所在线程查表,如果查不到用户创建的cxxxx 对应对象,它会创建一个临时对象出来。由于你在工作线程中调用该方法,当然不可能查到界面主线程中你所建立起来的那个对象了。这时mfc会你创建一个临时对象并返回给你,你根本不可能期望它的成员变量会是有意义的。 所以要用也只能用cwnd::fromhandle,因为它只包含一个m_hwnd成员。

 不过,要记住 跨线程直接或间接地调用::sendmessage,通常都是行为不可预测的。

1.工作线程给主线程发消息使用的是SendMessage和PoseMessage函数。这两个函数的区别在于SendMessage函数是阻塞方式,而PoseMessage函数是非阻塞方式。如果不是严格要求工作线程与主线程必须同步执行,则推荐使用PoseMessage。

2.不要在线程函数体内操作MFC控件,因为每个线程都有自己的线程模块状态映射表,在一个线程中操作另一个线程中创建的MFC对象,会带来意想不到的问题。更不要在线程函数里,直接调用UpdataData()函数更新用户界面,这会导致程序直接crash。而应该通过发送消息给主线程的方式,在主线程的消息响应函数里操作控件。

3.在主线程中不要使用WaitForSingleObject和WaitForMultipleObjects两个函数等待线程退出,其原因就是有导致程序死锁的隐患,特别是线程函数里调用了SendMessage或是直接操作了MFC对象,更易出现此种现象。

为解决这一问题,微软特提供了一个函数,MsgWaitForMultipleObjects。 照着网上的例子,写了在主线程中等待单个线程退出的程序:

DWORD dRet=-2;

MSG msg;

 BOOL bWaitAll=FALSE;

int nWaitCount=2; //初始等待的线程数目

while (1)

{

        dRet=MsgWaitForMultipleObjects(1,句柄的指 针,bWaitAll,INFINITE,QS_ALLINPUT);

        if (dRet == WAIT_OBJECT_0 + 1)

         {

                    TRACE("收到消息,函数返回值为%d /n",dRet);

                    while (PeekMessage(&msg,NULL,0,0,PM_REMOVE))

                     {            

                                TranslateMessage(&msg);

                                DispatchMessage(&msg);

                    }

          }

          else if (dRet == WAIT_OBJECT_0 )

           {

                       TRACE("线程退出了/n");

                        break;

           }

}//end while

}

实际使用中,在主线程中使用WaitForMultipleObjects导致界面线程在子线程结束前失去响应,而使用MsgWaitForMultipleObjects则很好的解决了这个问题。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/nefeithu123/archive/2009/06/07/4250002.aspx

1尽量少的使用全局变量、static变量做共享数据,尽量使用参数传递对象。被参数传递的对象,应该只包括必需的成员变量。所谓必需的成员变量,就是必定会被多线程操作的。很多人图省事,会把this指针(可能是任意一个对象指针)当作线程参数传递,致使线程内部有过多的操作权限,对this中的参数任意妄为。整个程序由一个人完成,可能会非常注意,不会出错,但只要一转手,程序就会面目全非。当两个线程同时操作一个成员变量的时候,程序就开始崩溃了,更糟的是,这种错误很难被重现。(我就在郁闷这个问题,我们是几个人,把程序编成debug版,经过数天使用,才找到错误。而找到错误只是开始,因为你要证明这个bug被修改成功了,也非常困难。)其实,线程间数据交互大多是单向的,在线程回调函数入口处,尽可能的将传入的数据备份到局部变量中(当然,用于线程间通讯的变量不能这么处理),以后只对局部变量做处理,可以很好的解决这种问题。

2、在MFC中请慎用线程。因为MFC的框架假定你的消息处理都是在主线程中完成的。首先窗口句柄是属于线程的,如果拥有窗口句柄的线程退出了,如果另一个线程处理这个窗口句柄,系统就会出现问题。而MFC为了避免这种情况的发生,使你在子线程中调用消息(窗口)处理函数时,就会不停的出Assert错误,烦都烦死你。典型的例子就时CSocket,因为CSocket是使用了一个隐藏窗口实现了假阻塞,所以不可避免的使用了消息处理函数,如果你在子线程中使用CSocket,你就可能看到assert的弹出了。

3、不要在不同的线程中同时注册COM组件。两个线程,一个注册1.ocx, 2.ocx, 3.ocx, 4.ocx; 而另一个则注册5.ocx, 6.ocx, 7.ocx, 8.ocx,结果死锁发生了,分别死在FreeLibrary和DllRegisterServer,因为这8个ocx是用MFC中做的,也可能是MFC的Bug,但DllRegisterServer却死在GetModuleFileName里,而GetModuleFileName则是个API唉!如果有过客看到,恰巧又知道其原因,请不吝赐教。

4、不要把线程搞的那么复杂。很多初学者,恨不能用上线程相关的所有的函数,这里互斥,那里等待,一会儿起线程,一会儿关线程的,比起goto语句有过之而无不及。好的多线程程序,应该是尽量少的使用线程。这句话怎么理解呐,就是说尽量统一一块数据共享区存放数据队列,工作子线程从队列中取数据,处理,再放回数据,这样才会模块化,对象化;而不是每个数据都起一个工作子线程处理,处理完了就关闭,写的时候虽然直接,等维护起来就累了。

但最后我个人用的是采用全局变量来实现的,因为程序比较小,不多,使用一个全局变量。


MFC 用进度条在状态栏显示某个按钮响应函数的过程,并用多线程实时更新

因为写的一个按钮的响应函数在数据量大的情况下会出现未响应的情况,所以为了让人直观的可以觉得程序仍然还在运行,并实时显示函数的运行状态。 (1)首先MFC工程中新建一个类,这个类不是MFC类而是Gene...
  • HW140701
  • HW140701
  • 2016年09月01日 21:19
  • 2055

MFC如何获得线程的状态的方法

如何获得线程的状态的方法不管是Windows API还是MFC的CWinThread类都没有给出直接获得线程状态的接口或函数。线程的状态分为正在执行、挂起、已经结束三种。利用API函数GetExitC...
  • Tonny_007
  • Tonny_007
  • 2014年03月18日 14:18
  • 1201

如何去除MFC窗口的自动记忆功能 (如去除工具栏,菜单栏上次运行程序自动保存的内容)

BCG注册表清除: 第一次在CXXXApp::ExitInstance() 加入CleanState(), 运行一次 接着注释掉,再在OnInitInstance加入m_bSaveState=F...
  • u013602835
  • u013602835
  • 2015年04月07日 15:05
  • 1602

MFC创建工作者线程.doc

  • 2010年03月09日 18:56
  • 11KB
  • 下载

MFC工作者线程实例

  • 2017年04月17日 17:03
  • 61KB
  • 下载

MFC创建多线程(工作者线程)demo

  • 2016年04月06日 09:57
  • 3.71MB
  • 下载

MFC多线程之工作者线程

1 2
  • wwkaven
  • wwkaven
  • 2014年09月04日 10:21
  • 548

MFC 界面线程和工作者线程--非模态对话框

每个系统都有线程,而线程的最重要的作用就是并行处理,提高软件的并发率。针对界面来说,还能提高界面的响应力。  线程分为界面线程和工作者线程,界面实际就是一个线程画出来的东西,这个线程维护一个“消息队列...
  • xiuxiaoman
  • xiuxiaoman
  • 2013年09月17日 14:13
  • 1520

MFC多线程 工作者线程 用户界面线程

  • 2014年09月04日 12:26
  • 144KB
  • 下载

MFC 界面线程和工作者线程

MFC 界面线程和工作者线程   每个系统都有线程,而线程的最重要的作用就是并行处理,提高软件的并发率。针对界面来说,还能提高界面的响应力。  线程分为界面线程和工作者线程,界...
  • yanzhibo
  • yanzhibo
  • 2013年05月01日 16:06
  • 2025
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:MFC中工作者线程调用状态栏,死掉原因
举报原因:
原因补充:

(最多只允许输入30个字)