您的位置:首页 > 其它

工作问题积累(二十三)CString在多线程下的安全问题

2014-07-10 09:29 260 查看




这个问题之前没碰到过,也没听过,有个朋友在群里提了这个问题,没人解答,对于问题不知道答案,我是睡不着觉的。

这篇博文对于我对CString安全问题的理解提到了很大的帮助,在网上查了很多相关问题,但是光看一篇文章还是不能完全明白,我想弄明白:(1.)导致安全问题的根源在哪里?为什么会出现?

【问题分析】:

参考上面博文中的CString的根源代码,正如里面所说,在Debug和Release模式下对于CString对象处理方式是不一样的,Debug模式下是new出来的,而Release模式下是定长内存。

里面演示了由于线程A没能够执行LeaveCriticalSection(&m_protect);线程B再次唤醒时就会发生堵塞。但是为什么会堵塞?我们得查一下EnterCriticalSection(&m_protect);这个函数的功能是什么

函数 EnterCriticalSection 和 LeaveCriticalSection 声明如下:

WINBASEAPIVOIDWINAPIEnterCriticalSection(    __inout LPCRITICAL_SECTION lpCriticalSection);

是多线程中用来确保同一时刻只有一个线程操作被保护的数据的操作函数,相关的多线程数据操作函数还有:

InitializeCriticalSection(&cs);//初始化临界区
EnterCriticalSection(&cs);//进入临界区//操作数据MyMoney*=10;//所有访问MyMoney变量的程序都需要这样写Enter..
Leave...LeaveCriticalSection(&cs);//离开临界区
DeleteCriticalSection(&cs);//删除临界区


多个线程操作相同的数据时,一般是需要按顺序访问的,否则会引导数据错乱,无法控制数据,变成随机变量。

为解决这个问题,就需要引入互斥变量,让每个线程都按顺序地访问变量。这样就需要使用EnterCriticalSection和LeaveCriticalSection函数。

这个过程实际上是通过限制有且只有一个函数进入CriticalSection变量来实现代码段同步的。简单地说,对于同一个 CRITICAL_SECTION,当一个线程执行了EnterCriticalSection而没有执行LeaveCriticalSection的时 候,其它任何一个线程都无法完全执行EnterCriticalSection而不得不处于等待状态。

CFixedAlloc::Alloc使用CPlex::Create完成空间申请,不过最显眼的是整个分配过程都在关键代码段内。与之对应的是空间的释放也同样在关键代码段内。在没看到源码前,我怎么也想不到CString会在这个地方使用了关键代码段。这意味着只要使用了CString,各个线程即便毫无关联,也会由此隐晦的联系起来。想像一下这种情况,两个线程都使用了CString对象,由于某种原因,线程A在CString的分配(或者释放)的过程中发生了意外,没有能够调用LeaveCriticalSection,那么线程B在使用CString的时候就会被“意外”的阻塞。

【解决办法】:

修改前:

CString strstate;
strstate.Format("正在解密,请稍后... (共 %d 张地图)",p->m_countmap);


修改后:

CWin32Heap stringHeap( HEAP_NO_SERIALIZE, 0, 0 );
CAtlStringMgr stringMgr( &stringHeap );
CString strstate(&stringMgr );
strstate.Format("正在解密,请稍后... (共 %d 张地图)",p->m_countmap);


为字符串数据自定义内存分配方案的最简单的方式是使用 ATL 提供的 CAtlStringMgr 类,但您需要自己提供内存分配例程。

CAtlStringMgr 的构造函数采用单一参数:即指向 IAtlMemMgr 对象的指针。IAtlMemMgr 是提供到堆的一般接口的抽象基类。通过IAtlMemMgr 接口,CAtlStringMgr 分配、重新分配和释放用于存储字符串数据的内存。您既可以自已实现IAtlMemMgr 接口,也可以使用由
ATL 提供的五个内存管理器类之一。ATL 提供的内存管理器只包装现有的内存分配功能:

CCRTHeap 包装标准 CRT 堆功能(malloc、free 和 realloc)
CWin32Heap 使用 HeapAlloc、HeapFree 和HeapRealloc 包装 Win32 堆句柄
CLocalHeap 包装 Win32 API:LocalAlloc、LocalFree 和LocalRealloc
CGlobalHeap 包装 Win32 API:GlobalAlloc、GlobalFree 和 GlobalRealloc
CComHeap 包装 COM 任务分配器 API:CoTaskMemAlloc、CoTaskMemFree 和 CoTaskMemRealloc

要进行字符串内存管理,最有用的类是 CWin32Heap,因为它使您能够创建多个独立的堆。例如,如果使用仅用于字符串的独立堆,可进行以下操作:

//Declare a thread-safe, growable, private heap with initial size 0
CWin32Heap g_stringHeap( 0, 0, 0 );
// Declare a string manager that uses the private heap
CAtlStringMgr g_stringMgr( &g_stringHeap );
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: