Posted on 2012-06-24 00:23
buf 阅读(840)
评论(0) 编辑 收藏 引用 所属分类:
MFC
MFC中的多线程编程
需要注意的是:“For size and performance reasons, MFC objects are not thread-safe at the object level, only at the class level. This means that you can have two separate threads manipulating two different CString objects, but not two threads manipulating the same CString object. If you absolutely must have multiple threads manipulating the same object, protect such access with appropriate Win32 synchronization mechanisms, such as critical sections.”。以上文字出自MSDN中Multithreading: Programming Tips一文。简单的说,从设计上MFC对象就不是线程安全的,如果必须跨越线程边界操作MFC对象,务必采取适当的同步机制。
对于某一些MFC对象(其中一个典型的代表是CWnd及其派生类),应该从两个层面来理解:首先它是一个c++类的一个实例,对应着内存中的一块区域(栈上分配或者new调用之后);另外它(可能)还关联着某些Windows资源(比如窗体/HWND,笔刷/HBRUSH,等等)。比如对于按钮CButton,提供了Create方法用来创建按钮窗体,并挂接(Attach)到发生该调用的CButton对象上。对于这一类MFC对象,它们和Windows资源的关联/映射在线程局部存储(Thread Local Storage, TLS)上实现的。这就意味着跨越线程边界操作这些MFC对象几乎是任何情况下都应该避免的,不仅仅是线程同步的考虑:操作在另一个线程中创建的MFC对象可能导致非预期的结果。“In a multi-threaded application written using MFC, you should not pass MFC objects across thread boundaries. As a general rule, a thread should access only those MFC objects that it creates. Failure to do so may cause run-time problems including assertions or unexpected program behavior. ”以上文字出自Microsoft Support中的Description of CWnd derived MFC objects and multithreaded applications in Visual C++一文。对此类问题一种正确的做法是传递句柄(对CWnd,即传递HWND),通过::PostMessage/::SendMessage使对MFC对象的操作代码在MFC对象创建线程中执行。
另外两点:
1. CWnd类提供了一个静态成员函数FromHandle,其原型为static CWnd* PASCAL FromHandle(HWND hWnd),看起来可以从HWND获得MFC对象。但是,“If a CWnd object is not attached to the handle, a temporary CWnd object is created and attached." 想象一下,如果有一个视图类CMyView,CMyView::OnInitialUpdate方法中创建了一个新线程,并把this->GetSafeHwnd()作为线程参数;在新线程中通过FromHandle获得一个指向CMyView对象的指针pView,结果会怎么样呢?完全没有问题,除了pView是个临时对象、与新线程创建代码发生上下文处的this毫无关系。
2. 顺便也说说CWnd::GetSafeHwnd(),与CWnd::m_hWnd有何区别呢?在VC8中可以找到CWnd::GetSafeHwnd()是定义在afxwin2.inl文件中的一个内联函数:
_AFXWIN_INLINE HWND CWnd::GetSafeHwnd() const
{ return this == NULL ? NULL : m_hWnd; }
乍一看为啥为出现this == NULL这样的检测呢?对于能够执行的代码,这个难道不是总是为True的吗?然而,其实下面的代码并不会出现编译错误,运行也不会出现内存违规:
class A
{
public:
void SafePrint()
{
if (this == NULL)
{
printf("object not exist\n");
}
else
{
printf("value=%d\n", m_value);
}
}
int m_value;
};
A *pa = NULL;
pa->SafePrint(); // this is totally ok!!!
for more infomation, please refer to:
Multithreading: Programming Tips,http://msdn.microsoft.com/en-us/library/h14y172e%28v=vs.71%29.aspx
TN003: Mapping of Windows Handles to Objects,http://msdn.microsoft.com/en-us/library/c251x6s1%28v=vs.71%29.aspx
Description of CWnd derived MFC objects and multithreaded applications in Visual C++,http://support.microsoft.com/kb/147578
Why do I get an assertion in MFC when my thread calls a function in the main thread? ,http://vcfaq.mvps.org/mfc/11.ht