關於VC的C運行時庫的多線程版本和單線程版本的比較和分析

    VC6和VC.net2003默認使用單線程(Single-threaded)的C運行時庫,到2005版本則默認爲多線程(Multi-threaded),到了2008,微軟索性把單線程運行時開關給去掉了.很多人對此很好奇,但不知道這兩個運行時庫具體有什麼區別.對此,本人從crt源代碼入手,以VC6的CRT爲例,分析結果如下:

 

VC編譯的Win32應用程序執行時,首先調用的並不是WinMain,而是C Runtime 的WinMainCRTStartup函數,


void WinMainCRTStartup( void )

{
        int mainret;

        /*
         * Get the full Win32 version
         */
        _osver = GetVersion();

        _winminor = (_osver >> 8) & 0x00FF ;
        _winmajor = _osver & 0x00FF ;
        _winver = (_winmajor << 8) + _winminor;
        _osver = (_osver >> 16) & 0x00FFFF ;

#ifdef _MT
        if ( !_heap_init(1) )               /* initialize heap */
#else  /* _MT */
        if ( !_heap_init(0) )               /* initialize heap */
#endif  /* _MT */
            fast_error_exit(_RT_HEAPINIT);  /* write message and die */

#ifdef _MT
        if( !_mtinit() )                    /* initialize multi-thread */
            fast_error_exit(_RT_THREAD);    /* write message and die */
#endif  /* _MT */

......

此處可以看到,兩種版本的運行時堆的初始化是不相同的,多線程使用1參數調用_heap_init()函數

下面看看_heap_init()如何初始化堆

int __cdecl _heap_init (
        int mtflag
        )
{
        //  Initialize the "big-block" heap first.
        if ( (_crtheap = HeapCreate( mtflag ? 0 : HEAP_NO_SERIALIZE,
                                     BYTES_PER_PAGE, 0 )) == NULL )

            return 0;

        // Pick a heap, any heap
        __active_heap = __heap_select();

        if ( __active_heap == __V6_HEAP )
        {
            //  Initialize the small-block heap
            if (__sbh_heap_init(MAX_ALLOC_DATA_SIZE) == 0)
            {
                HeapDestroy(_crtheap);
                return 0;
            }
        }
        else if ( __active_heap == __V5_HEAP )
        {
            if ( __old_sbh_new_region() == NULL )
            {
                HeapDestroy( _crtheap );
                return 0;
            }
        }

        return 1;
}

可以看到,不同點在於單線程使用了HEAP_NO_SERIALIZE參數調用了

HANDLE HeapCreate(
  DWORD
flOptions,       // heap allocation attributes
  SIZE_T dwInitialSize// initial heap size
  SIZE_T dwMaximumSize   // maximum heap size
);

函數,msdn中對於HEAP_NO_SERIALIZE是這樣描述的,

HEAP_NO_SERIALIZE Specifies that mutual exclusion will not be used when the heap functions allocate and free memory from this heap. The default, when HEAP_NO_SERIALIZE is not specified, is to serialize access to the heap. Serialization of heap access allows two or more threads to simultaneously allocate and free memory from the same heap.

可見,多線程運行時庫創建的堆允許兩個以上線程在相同的堆中同時分配和釋放內存.之後多線程庫調用

int __cdecl _mtinit (
        void
        )
{
        _ptiddata ptd;


        /*
         * Initialize the mthread lock data base
         */

        _mtinitlocks();

        /*
         * Allocate a TLS index to maintain pointers to per-thread data
         */
        if ( (__tlsindex = TlsAlloc()) == 0xffffffff )
            return FALSE;       /* fail to load DLL */


        /*
         * Create a per-thread data structure for this (i.e., the startup)
         * thread.
         */
        if ( ((ptd = _calloc_crt(1, sizeof(struct _tiddata))) == NULL) ||
             !TlsSetValue(__tlsindex, (LPVOID)ptd) )
            return FALSE;       /* fail to load DLL */

        /*
         * Initialize the per-thread data
         */

        _initptd(ptd);

        ptd->_tid = GetCurrentThreadId();
        ptd->_thandle = (unsigned long)(-1L);


        return TRUE;
}


void __cdecl _mtinitlocks (
        void
        )
{


        /*
         * All we need to do is initialize _LOCKTAB_LOCK and _EXIT_LOCK1.
         */
        InitializeCriticalSection( _locktable[_LOCKTAB_LOCK] );
        InitializeCriticalSection( _locktable[_EXIT_LOCK1] );
        InitializeCriticalSection( _locktable[_HEAP_LOCK] );
        InitializeCriticalSection( _locktable[_SIGNAL_LOCK] );

}
關於InitializeCriticalSection函數解釋如下

InitializeCriticalSection

The InitializeCriticalSection function initializes a critical section object.

VOID InitializeCriticalSection(
  LPCRITICAL_SECTION   // critical section
);

Parameters

lpCriticalSection
[out] Pointer to the critical section object.

Return Values

This function does not return a value.

In low memory situations, InitializeCriticalSection can raise a STATUS_NO_MEMORY exception.

Remarks

The threads of a single process can use a critical section object for mutual-exclusion synchronization. There is no guarantee about the order in which threads will obtain ownership of the critical section, however, the system will be fair to all threads.

The process is responsible for allocating the memory used by a critical section object, which it can do by declaring a variable of type CRITICAL_SECTION. Before using a critical section, some thread of the process must call the InitializeCriticalSection or InitializeCriticalSectionAndSpinCount function to initialize the object.

After a critical section object has been initialized, the threads of the process can specify the object in the EnterCriticalSection, TryEnterCriticalSection, or LeaveCriticalSection function to provide mutually exclusive access to a shared resource. For similar synchronization between the threads of different processes, use a mutex object.

A critical section object cannot be moved or copied. The process must also not modify the object, but must treat it as logically opaque. Use only the critical section functions to manage critical section objects. When you have finished using the critical section, call the DeleteCriticalSection function.

A critical section object must be deleted before it can be reinitialized. Initializing a critical section that has already been initialized results in undefined behavior.

 

void __cdecl _initptd (
        _ptiddata ptd
        )
{
        ptd->_pxcptacttab = (void *)_XcptActTab;
        ptd->_holdrand = 1L;

#ifdef _M_MRX000
        /*
         * MIPS per-thread data
         */
        ptd->_MipsPtdDelta =
        ptd->_MipsPtdEpsilon = -1L ;
#endif  /* _M_MRX000 */
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章