工作线程中调用UI线程创建的窗口的UpdateData会导致Assert的问题及解决办法(ZZ)

最近写了个代码,在UI线程中创建了一个窗口,然后在工作线程中修改了这个窗口中的一些数据,然后想用UpdateData(FALSE)来更新窗口的内容,结果在Debug版本下面就出现了Assert报错,说出错地方是wincore.cpp的888行和889行,就是这两句
  ASSERT((p = pMap->LookupPermanent(m_hWnd)) != NULL ||
   (p = pMap->LookupTemporary(m_hWnd)) != NULL);
  ASSERT((CWnd*)p == this);   // must be us
    我用MFC也没有多久,也不太熟悉,翻了翻资料,在 http://support.microsoft.com/default.aspx?scid=kb;en-us;147578找到一篇文章,就是说MFC窗口跨线程的问题的,大概意思就是MFC的窗口是线程相关的,每个窗口的HandleMap是储存在线程相关的堆栈里面的(thread-local-storage (TLS) ),那这样我就理解了为什么上面两句ASSERT会出错了,线程环境都切换了当然线程堆栈的数据也就不一样了.
    这篇文章提供了两种修改方案:
    一种是用FromHandle来获得一个CWnd*,然后再调用UpdateData,这个方案我没有实验成功,结果是错虽然不报了,但是界面也没有被更新.
    另外一种是通过发消息的方法转到UI线程去处理.可以在窗口映射一个消息,比如ON_MESSAGE(WM_UPDATEDATA, OnUpdateData),然后用SendMessage(WM_UPDATEDATA, FALSE)传消息给窗口,窗口的消息处理肯定是在UI线程里面,这时候可以用
LRESULT CProtectPage::OnUpdateData(WPARAM wParam, LPARAM lParam)
{
 UpdateData(wParam);
 return 0;
}
来更新界面,实验是成功的,ASSERT就被消除了.
    还是有点疑惑,就是刚开始直接在工作线程中调用UpdateData(FALSE)的时候,虽然有ASSERT报错,但是结果还是正确的,似乎没有什么影响,不知道这个ASSERT到底意味着什么?

2. 线程与GDI的冲突:死机的主要原因

很多人使用线程的时候,都喜欢在线程内画图。如果在线程内作画,程序就会很容易出错,而且还是那种没有任何响应和提示的错误问题。

例如,如下是一个文件复制的程序,这个程序由两个线程组成,一个是复制文件的线程,另一个是显示文件复制进度的过程。当文件复制一部分后,进度条就向前移动一点。理论上,这个程序没什么问题。但是,这个程序有一个很大的隐患,即主程序也可能某一时刻要更新这个进度条。例如,进度被其他窗口挡住后或者整个窗口放大缩小时,整个窗口就要刷新,这时,线程的那个部分也要刷新它,操作系统也要刷新它。这样,三个部分都要去刷新它,程序就很容易死锁。程序运行界面如图所示。


程序运行界面图

这时会什么响应也没有了。这种问题在多线程中是很常见的。那怎么处理这个问题呢?

有一条原则,即程序中的线程一概不直接操作线程部分中的GDI。它只要发一个消息给主程序,让主程序来绘制图形,就不会出现任何的问题了。

发送消息的方法就是用PostMessage的函数。但一定不能用SendMessage。因为用PostMessage可以让主程序去调度绘图,而SendMesage会立即去绘制图形。所以在线程中要避免画图,因为当作画时,程序会取得一个DC,内存中的DC表示的是一块显存。DC代表的是一个窗口,因为一个程序得到此DC时,其他程序是不能再取得DC的。以后,如果继续再取,就会进入死锁的循环内。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值