關于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類庫
|