MFC线程安全问题

原创 2007年09月28日 14:10:00

MFC线程安全问题
最近在工作中使用mfc,发现一个问题,由于使用mfc不久,总结了以下,如下:
问题:
在KdvDecoder(使用的解码库)中,提供文件解码器功能,其中提供一个进度上报回调函数:
typedef void (*pfFileStatCB)(u8 byFileState, u32 dwSec, u32 dwContext) ; 
// 回调函数,dwSec == (u32)-1 代表文件播放结束
CU在实现回调时,代码如下:
void CVideoPlayerDlg::FileStatCB(u8 byFileState, u32 dwSec, u32 dwContext)
{
 CVideoPlayerDlg *pVideoPlayerDlg = (CVideoPlayerDlg*)dwContext;
    // 文件播放结束,发送结束消息给播放窗口
 if((u32)-1 == dwSec) 
 {
  ::PostMessage(pVideoPlayerDlg->GetSafeHwnd(), IDC_BTN_STOP, 0, 0);
  return ;
 }
    // 设置当前进度
 pVideoPlayerDlg->m_staticProcess.SetScrollPos(dwSec);
    // 设置播放总时间和当前播放时间
 ……
 ……计算播放总时间和当前播放时间
 //在窗口显示   当前时间/总时间
 pVideoPlayerDlg->m_staticStatus.SetWindowText(strProcTime);
}
在播放过程中,拖拉进度条程序出现死锁现象。
解决方法:
void CVideoPlayerDlg::FileStatCB(u8 byFileState, u32 dwSec, u32 dwContext)
{
 //发送进度消息到播放窗口
 ::PostMessage((HWND)dwContext, WM_FILEPROGRESS_NOTIRY,
     (WPARAM)byFileState, (LPARAM)dwSec);
}
ON_MESSAGE(WM_FILEPROGRESS_NOTIRY, OnFileProgress)
void CVideoPlayerDlg::OnFileProgress(WPARAM wParam, LPARAM lParam)
{
 u8 byFileStatus = (u8)wParam;
 u32 dwSec = (u32)lParam;

 if((u32)-1 == dwSec)
 {
  OnBtnStop();
  return ;
 }
……
 ……计算播放总时间和当前播放时间
 m_staticStatus.SetWindowText(strProcTime);
}

原因:在线程间传递大多数MFC类的指针,是一个常识性的错误。。
解决方法:在多线程中传递窗口对象句柄。

MFC规定:
不能从一个非MFC线程创建和访问MFC对象,如果一个线程被创建时没有用到CWinThread对象,比如,直接使用“C”的_beginthread或者_beginthreadex创建的线程,则该线程不能访问MFC对象;换句话说,只有通过CWinThread创建MFC线程对象和Win32线程,才可能在创建的线程中使用MFC对象。
一个线程仅仅能访问它所创建的MFC对象。

这两个规定的原因是:
MFC对象和Windows对象之间有一个一一对应的关系,这种关系以映射的形式保存在创建线程的当前模块的模块-线程状态信息中。当一个线程使用某个MFC对象指针P时,ASSERT_VALID(P)将验证当前线程的当前模块是否有Windows句柄和P对应,即是否创建了P所指的Windows对象,验证失败导致ASSERT断言中断程序的执行。如果一个线程要使用其他线程的Windows对象,则必须传递Windows对象句柄,不能传递MFC对象指针。
一般来说,MFC应用程序仅仅在Debug版本下才检查这种映射关系,所以访问其他线程的MFC对象的程序在Realease版本下表面上不会有问题,但是MFC对象被并发访问的后果是不可预见的。
实现MFC对象和Windows对象之间的映射,MFC提供了几个函数完成MFC对象和Windows对象之间的映射或者解除这种映射关系,以及从MFC对象得到Windows对象或者从Windows对象得到或创建相应的MFC对象。
每一个MFC对象类都有成员函数Attach和Detach,FromHandle和FromHandlePermanent,AssertValid。这些成员函数的形式如下:
Attach(HANDLE Windows_Object_Handle)
例如:CWnd类的是Attach(HANLDE hWnd),CDC类的是Attach(HDC hDc)。
Attach用来把一个句柄永久性(Perment)地映射到一个MFC对象上:它把一个Windows对象捆绑(Attach)到一个MFC对象上。
Detach()
Detach用来取消Windows对象到MFC对象的永久性映射。如果该Windows对象有一个临时的映射存在,则Detach不理会它。MFC让线程的Idle清除临时映射和临时MFC对象。
FromHandle(HANDLE Windows_Object)
它是一个静态成员函数。如果该Windows对象没有映射到一个MFC对象,FromHandle则创建一个临时的MFC对象,并把Windows对象映射到临时的MFC对象上,然后返回临时MFC对象。
FromHandlePermanent(HANDLE Windows_Object)
它是一个静态成员函数。如果该Windows对象没有永久地映射到一个MFC对象上,则返回NULL,否则返回对应的MFC对象。
AssertValid()
它是从CObject类继承来的虚拟函数。MFC覆盖该函数,实现了至少一个功能:判断当前MFC对象的指针this是否映射到一个对应的可靠的Windows对象。

总结:
1、MFC的映射数据保存在模块-线程状态中,是线程和模块局部的。每个线程管理自己映射的数据,其他线程不能访问到本线程的映射数据,也就不允许使用本线程的MFC对象。
2、每一个MFC对象类(CWnd、CDC等)负责创建或者管理这类线程-模块状态的对应CHandleMap类对象。例如,CWnd::Attach创建一个永久性的映射保存在m_pmapHwnd所指对象中,如果m_pmapHand还没有创建,则使用AfxMapHWND创建相应的CHandleMap对象。
3、需要跨线程时解决方法:在多线程中传递窗口对象句柄,。
 

Java多线程技术初识——4. 多线程安全问题的解决

多线程安全问题的解决方案: 首先我们得明白两个概念:同步 和 互斥; 同步——这是线程 or  进程之间的 合作关系。对多个线程在执行次序上进行协调,以使并发的各个线程能更好的利共享资源,相互...
  • watermusicyes
  • watermusicyes
  • 2013年04月14日 23:20
  • 3463

线程同步:解决线程不安全问题

当多个线程并发访问同一个资源对象时,可能会出现线程不安全的问题,比如现有50个苹果,现在有请三个童鞋(小A,小B,小C)上台表演吃苹果.因为A,B,C三个人可以同时吃苹果,此时使用多线程技术来实现这个...
  • caidie_huang
  • caidie_huang
  • 2016年10月07日 12:30
  • 1438

MFC不能多线程操作控件的原因

表现错误示例 网友hewwatt大致原因解释如下 原因分析 窗口类 MFC状态 模块本地数据 进程本地数据 线程本地数据 模块线程状态 包装类对象和句柄映射 解决办法 注意事项  对于大多数mfc对象...
  • XscKernel
  • XscKernel
  • 2016年05月17日 15:55
  • 2478

Stack 线程安全问题

  • 2010年07月20日 11:44
  • 1KB
  • 下载

servlet与Struts action线程安全问题分析(pdf)

  • 2008年06月24日 14:49
  • 130KB
  • 下载

servlet与Struts action线程安全问题分析

  • 2012年02月29日 16:40
  • 46KB
  • 下载

第一节(多线程通讯代码演示及解决线程安全问题)

  • 2017年08月23日 08:18
  • 63.14MB
  • 下载

Java线程安全问题_动力节点Java学院整理

  • 2017年11月08日 16:03
  • 195KB
  • 下载

unix_linux线程安全问题

  • 2013年07月18日 10:34
  • 327KB
  • 下载

Java线程安全问题,方法及内存模型

最近想将java基础的一些东西都整理整理,写下来,这是对知识的总结,也是一种乐趣。已经拟好了提纲,大概分为这几个主题: java线程安全,java垃圾收集,java并发包详细介绍,java profi...
  • a519781181
  • a519781181
  • 2016年05月31日 04:09
  • 269
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:MFC线程安全问题
举报原因:
原因补充:

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