网站首页/技术开发列表/内容

对于CString的随想

技术开发2022-07-15阅读
关于CString的随想
  CString比起STL的string来说,有很多方便的地方,我特别喜欢它的Format,呵呵。有人觉得使用CString效率会低,那只有一种情况,就是发生堆争用的时候,其它情况下的效率影响,比方说几个字符串的连接等等操作,会构造很多临时对象,我认为这样的效率影响根本就不值得一提。本身CString的内部做了很多工作了,有的时候我们还是要相信微软的这条基础内裤的(MFC——微软基础类库,呵呵,谐音,基础内裤J)。


  由 MFC 和 ATL 提供的默认字符串管理器是全局堆顶上的简单包装。该全局堆是完全线程安全的,即多线程可同时从中分配和释放内存而不会损坏堆。为了确保线程的安全,堆必须将对它的访问进行序列化。实现这一点时通常会用到临界区或类似的锁定机制。每当两个线程试图同时访问堆时,将阻塞其中的一个线程,直至完成另一个线程的请求。对于许多应用程序,这种情况很少发生,并且堆锁定机制带来的对性能的影响可以忽略不计。但是,如果应用程序从堆锁定的多线程争用中频繁访问堆,那么可能造成应用程序的运行速度慢于从单线程访问(即使是在带有多个 CPU 的计算机上运行也是如此)。


  使用 CStringT 的应用程序更加容易受堆争用的影响,因为在 CStringT 对象上的操作会频繁要求对字符串缓冲区进行重新分配。


  减轻线程间堆争用的一个方式是为每个线程从专用的、本地线程堆分配字符串。只要用特定线程分配器分配的字符仅用于该线程中,那么该分配器就不需要线程安全。以下示例阐释了一个线程过程,该线程过程为自身分配一个专用的非线程安全堆以供该线程上的字符串使用:



  DWORD_PTR WINAPI WorkerThreadProc( void* pContext )
  {
   // Declare a non-thread-safe heap just for this thread
   CWin32Heap stringHeap( HEAP_NO_SERIALIZE, 0, 0 );
  
   // Declare a string manager that uses the thread's heap
   CAtlStringMgr stringMgr( &stringHeap );
  
   int n = 1;
   for( int nPower = 0; nPower < 10;="" npower++="">
  {
   // Use the thread's string manager, instead of the default
   CString strPower( &stringMgr );
  
   strPower.Format( "%d", n );
   printf( "%s\n", LPCSTR( strPower ) );
   n *= nBase;
  }
  
   return( 0 );
  }


  使用同样的线程过程可以运行多线程,但是由于每个线程都有自已的堆,所以线程之间不存在争用。此外,由于每个堆都不是线程安全的,因此带来了性能上的明显提升,即使只运行线程的一个副本也如此。对于不使用昂贵的互锁操作来避免并发访问的堆,可以使用这种方法。


  对于更为复杂的线程过程,较方便的做法是将指向线程的字符串管理器的指针存储在线程本地存储 (TLS) 槽中。这使得其他由线程过程调用的函数能够访问该线程的字符串管理器。


  所以MSDN不推荐在很长的循环里操作CString,就是这个原因,除非你向上面的例子一样自己为CString分配一个局部的堆,因为是线程自己使用,所以根本不用考虑线程安全。


  VC6和VC7的类库相差很大,我这里讨论的是基于VC7的MFC类库

……

相关阅读