日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

線(xiàn)程學(xué)習(xí)

 seman 2009-10-30

      對(duì)于了解任何一種事物,最容易也最常見(jiàn)的方法自然是先學(xué)習(xí)他的基礎(chǔ)知識(shí),先知其然。循序漸進(jìn),這很重要。對(duì)于線(xiàn)程當(dāng)然也不例外。那么線(xiàn)程會(huì)有哪些基礎(chǔ)的知 識(shí)要了解呢?我在這里不會(huì)大篇的介紹那些API如何使用,也不會(huì)介紹一些庫(kù)的使用,比如MFC啥的。要了解線(xiàn)程的基礎(chǔ)知識(shí),我想只需要回答四個(gè)問(wèn)題即可:

        1.什么是線(xiàn)程?

        2. 什么時(shí)候應(yīng)該用多線(xiàn)程?

        3. 什么時(shí)候又不能用多線(xiàn)程?

        4. 如何創(chuàng)建線(xiàn)程?

        5. 如何終止線(xiàn)程?

     回答了這五個(gè)問(wèn)題,對(duì)于線(xiàn)程自然會(huì)有了基本的了解,那么你也就進(jìn)入了線(xiàn)程的世界。首先我們來(lái)看第一個(gè)問(wèn)題:

         什么是線(xiàn)程?

         每個(gè)進(jìn)程至少包含一個(gè)線(xiàn)程,一個(gè)進(jìn)程包括一個(gè)進(jìn)程對(duì)象,一個(gè)地址空間。同樣線(xiàn)程由兩個(gè)部分組成:線(xiàn)程內(nèi)核對(duì)象和線(xiàn)程棧。操作系統(tǒng)通過(guò)線(xiàn)程內(nèi)核對(duì)象管理線(xiàn) 程,同時(shí)內(nèi)核對(duì)象也是系統(tǒng)用來(lái)存放線(xiàn)程統(tǒng)計(jì)信息的地方。線(xiàn)程棧用于維護(hù)線(xiàn)程在執(zhí)行代碼時(shí)需要的所有函數(shù)參數(shù)和局部變量。進(jìn)程是從來(lái)不執(zhí)行任何東西的,他只 是線(xiàn)程依存的地方。線(xiàn)程總是在某個(gè)進(jìn)程中創(chuàng)建,并且整個(gè)生命周期都在進(jìn)程中。也就是說(shuō),線(xiàn)程在他的進(jìn)程地址空間中執(zhí)行代碼,對(duì)相同的數(shù)據(jù)進(jìn)行操作。當(dāng)然還 能共享內(nèi)核對(duì)象句柄。因?yàn)閮?nèi)核對(duì)象句柄表是依賴(lài)于進(jìn)程而不是線(xiàn)程。
       很多人都知道線(xiàn)程比進(jìn)程“重”,為什么呢?因?yàn)椋?/p>

        1. 進(jìn)程需要更多的地址空間所以進(jìn)程使用的系統(tǒng)資源比線(xiàn)程多很多。為進(jìn)程創(chuàng)建一個(gè)虛擬地址空間需要很多系統(tǒng)資源。需要占用大量的內(nèi)存來(lái)保留大量記錄。
        2. 由于.exe或.dll文件要加載到一個(gè)弟子空間,所以需要文件資源。而線(xiàn)程使用的系統(tǒng)資源少很多。

         什么時(shí)候應(yīng)該用線(xiàn)程?

         用一句話(huà)來(lái)回答就是:當(dāng)你想一心二用的時(shí)候你就應(yīng)該使用多線(xiàn)程!比如我現(xiàn)在想一邊敲這些字一邊和可樂(lè)就得用多線(xiàn)程。在你炒菜的同時(shí)又要燒水也得用多線(xiàn)程 (并發(fā))。。當(dāng)你很用心在寫(xiě)一段很長(zhǎng)的代碼的時(shí)候,如果別人在這個(gè)時(shí)候叫你,你不希望聽(tīng)不見(jiàn)那么也得用多線(xiàn)程(防止阻塞,UI假死)。再比如分房的年代, 如果是按照人頭分房,你希望分的房子大一點(diǎn)那么你也得用多線(xiàn)程(可能會(huì)獲得更多的CPU時(shí)間片,特別是在多核上)。如果你是一個(gè)創(chuàng)業(yè)者,當(dāng)你的公司漸漸的 長(zhǎng)大,人越來(lái)越多,事情也越來(lái)越復(fù)雜的時(shí)候,你希望不同的人去做不同的事情,你希望把更多的資源給重要的人,而不希望(至少希望不是很多)那些不太重要的 事情占用你某些寶貴的資源的時(shí)候,你也得用多線(xiàn)程(優(yōu)先級(jí))。因此我認(rèn)為有四種情況,我們是需要使用多線(xiàn)程的。即:

                               1. 有多件事情,順序執(zhí)行無(wú)法滿(mǎn)足的時(shí)候;

                               2. 在處理長(zhǎng)時(shí)間的事情(算法)時(shí)為了防止應(yīng)用界面(UI)不響應(yīng)用戶(hù)輸入,造成UI假死的時(shí)候;比如大圖像渲染、大數(shù)據(jù)處理/排序、搜索等

                               3. 為了通過(guò)獲得更多的CPU時(shí)間片來(lái)提高程序效率的時(shí)候;

                               4. 需要同時(shí)處理的事情有優(yōu)先級(jí)別的時(shí)候;應(yīng)該使用高優(yōu)先級(jí)線(xiàn)程管理對(duì)時(shí)間要求很急的任務(wù),而使用低優(yōu)先級(jí)線(xiàn)程執(zhí)行被動(dòng)任務(wù)或者對(duì)時(shí)間不敏感的任務(wù)。高

        什么時(shí)候不應(yīng)該使用多線(xiàn)程?

        非常重要也是初學(xué)多線(xiàn)程編程很容易犯錯(cuò)誤的一點(diǎn)就是不要將你的程序的界面(窗口)放到不同的線(xiàn)程中去。除非你能做到像explorer那么好。還有編輯/打印問(wèn)題。當(dāng)然都不是絕對(duì)的。關(guān)鍵是要用好,不能隨便用。

        如何創(chuàng)建線(xiàn)程?
        線(xiàn)程都必須有一個(gè)入口函數(shù),線(xiàn)程從這個(gè)入口開(kāi)始運(yùn)行。主線(xiàn)程的入口函數(shù)可以為:main,wmain,WinMain,wWinMain.創(chuàng)建的輔助線(xiàn)程也必須有入口函數(shù),函數(shù)形式:

DWORD WINAPI ThreadFunc(PVOID pvParam)

{
     DWORD dwResult = 0;
    ……
    return dwResult ;
}
在線(xiàn)程函數(shù)中必須返回一個(gè)值,他將成為該進(jìn)程的代碼。而且應(yīng)該盡可能使用參數(shù)和局部變量。當(dāng)使用靜態(tài)變量和全局變量時(shí),多個(gè)線(xiàn)程可以同時(shí)訪(fǎng)問(wèn)這些變量,就可能會(huì)破壞變量的內(nèi)存。但是參數(shù)和局部變量是在線(xiàn)程棧中創(chuàng)建的,所以不太可能別其他線(xiàn)程破壞。
       創(chuàng)建線(xiàn)程使用CreateThread函數(shù)。這是系統(tǒng)提供的唯一一個(gè)創(chuàng)建線(xiàn)程的函數(shù)。這個(gè)函數(shù)首先創(chuàng)建一個(gè)線(xiàn)程內(nèi)核對(duì)象,如何從進(jìn)程的地址空間分配內(nèi)存供 線(xiàn)程使用。線(xiàn)程可以訪(fǎng)問(wèn)進(jìn)程的內(nèi)核對(duì)象的所有句柄、進(jìn)程中的所有內(nèi)存以及同進(jìn)程的其他線(xiàn)程的棧。因此同進(jìn)程中多個(gè)線(xiàn)程能夠非常容易相互通信。雖然 CreateThread是系統(tǒng)提供的唯一一個(gè)創(chuàng)建線(xiàn)程的函數(shù),但是如果你是使用C/C++編寫(xiě)多線(xiàn)程,卻不能使用這個(gè)函數(shù),而是應(yīng)該使用編譯器提供的替 代函數(shù)。比如VC提供的是_beginthreadex函數(shù)??赡苣銜?huì)問(wèn)為什么。要回答這個(gè)問(wèn)題,得從c運(yùn)行庫(kù)說(shuō)起。C運(yùn)行庫(kù)是1970年問(wèn)世的。那時(shí)候 還沒(méi)有任何線(xiàn)程的應(yīng)用,因此,C運(yùn)行庫(kù)自然是不支持多線(xiàn)程的。存在問(wèn)題的函數(shù)包括 errno,_doserrno,strtok,_wcstok,strerror,_strerror,tmpnam,tmpfile,asctime,_wasctime,gmtime,_ecvt 和_fcvt等。存在問(wèn)題是因?yàn)檫@些內(nèi)容都是活類(lèi)似全局變量,線(xiàn)程直接是相互覆蓋的,也就是不是線(xiàn)程安全的。要解決也很容易。只要?jiǎng)?chuàng)建一個(gè)數(shù)據(jù)結(jié)構(gòu)保存這 些內(nèi)容,并將他和線(xiàn)程關(guān)聯(lián)就可以了。但是問(wèn)題也來(lái)了,系統(tǒng)又不知道你不安全,甚至連你是不是C/C++程序都不知道。_beginthreadex函數(shù)是 vc的crt函數(shù)。幸好MS提供了源代碼。看看源代碼我們就知道一切了。下面是從VC8的crt中的threadex.c文件中拷貝而來(lái)。

_MCRTIMP uintptr_t __cdecl _beginthreadex (

        void *security,

        unsigned stacksize,

        unsigned (__CLR_OR_STD_CALL * initialcode) (void *),

        void * argument,

        unsigned createflag,

        unsigned *thrdaddr

        )

{

        _ptiddata ptd;                  /* pointer to per-thread data */

        uintptr_t thdl;                 /* thread handle */

        unsigned long err = 0L;     /* Return from GetLastError() */

        unsigned dummyid;               /* dummy returned thread ID */

        /* validation section */

        _VALIDATE_RETURN(initialcode != NULL, EINVAL, 0);

        /* Initialize FlsGetValue function pointer */

        __set_flsgetvalue();

        /*

         * Allocate and initialize a per-thread data structure for the to-

         * be-created thread.

         */

        if ( (ptd = (_ptiddata)_calloc_crt(1, sizeof(struct _tiddata))) == NULL )

                goto error_return;

        /*

         * Initialize the per-thread data

         */

        _initptd(ptd, _getptd()->ptlocinfo);

        ptd->_initaddr = (void *) initialcode;

        ptd->_initarg = argument;

        ptd->_thandle = (uintptr_t)(-1);

#if defined (_M_CEE) || defined (MRTDLL)

        if(!_getdomain(&(ptd->__initDomain)))

        {

            goto error_return;

        }

#endif  /* defined (_M_CEE) || defined (MRTDLL) */

        /*

         * Make sure non-NULL thrdaddr is passed to CreateThread

         */

        if ( thrdaddr == NULL )

                thrdaddr = &dummyid;

        /*

         * Create the new thread using the parameters supplied by the caller.

         */

        if ( (thdl = (uintptr_t)

              CreateThread( (LPSECURITY_ATTRIBUTES)security,

                            stacksize,

                            _threadstartex,

                            (LPVOID)ptd,

                            createflag,

                            (LPDWORD)thrdaddr))

             == (uintptr_t)0 )

        {

                err = GetLastError();

                goto error_return;

        }

        /*

         * Good return

         */

        return(thdl);

        /*

         * Error return

         */

error_return:

        /*

         * Either ptd is NULL, or it points to the no-longer-necessary block

         * calloc-ed for the _tiddata struct which should now be freed up.

         */

        _free_crt(ptd);

        /*

         * Map the error, if necessary.

         *

         * Note: this routine returns 0 for failure, just like the Win32

         * API CreateThread, but _beginthread() returns -1 for failure.

         */

        if ( err != 0L )

                _dosmaperr(err);

        return( (uintptr_t)0 );

}
從代碼中清楚的看到每個(gè)線(xiàn)程都有一個(gè)屬于自己的tiddata數(shù)據(jù)結(jié)構(gòu)。傳進(jìn)來(lái)的參數(shù)和現(xiàn)場(chǎng)函數(shù)地址都保存在這里。當(dāng)然他正如我們預(yù)料的一樣最終調(diào)用了 CreateThread函數(shù)。只是沒(méi)有把傳進(jìn)來(lái)的線(xiàn)程函數(shù)和參數(shù)傳給CreateThread。而是_threadstartex和tiddata。下 面我們接下來(lái)看一下這兩個(gè)東西。tiddata位于mtdll.h,_threadstartex和_beginthread在同一個(gè)文件。

/* Structure for each thread’s data */

struct _tiddata {

    unsigned long   _tid;       /* thread ID */

    uintptr_t _thandle;         /* thread handle */

    int     _terrno;            /* errno value */

    unsigned long   _tdoserrno; /* _doserrno value */

    unsigned int    _fpds;      /* Floating Point data segment */

    unsigned long   _holdrand;  /* rand() seed value */

    char *      _token;         /* ptr to strtok() token */

    wchar_t *   _wtoken;        /* ptr to wcstok() token */

    unsigned char * _mtoken;    /* ptr to _mbstok() token */

    /* following pointers get malloc’d at runtime */

    char *      _errmsg;        /* ptr to strerror()/_strerror() buff */

    wchar_t *   _werrmsg;       /* ptr to _wcserror()/__wcserror() buff */

    char *      _namebuf0;      /* ptr to tmpnam() buffer */

    wchar_t *   _wnamebuf0;     /* ptr to _wtmpnam() buffer */

    char *      _namebuf1;      /* ptr to tmpfile() buffer */

    wchar_t *   _wnamebuf1;     /* ptr to _wtmpfile() buffer */

    char *      _asctimebuf;    /* ptr to asctime() buffer */

    wchar_t *   _wasctimebuf;   /* ptr to _wasctime() buffer */

    void *      _gmtimebuf;     /* ptr to gmtime() structure */

    char *      _cvtbuf;        /* ptr to ecvt()/fcvt buffer */

    unsigned char _con_ch_buf[MB_LEN_MAX];

                                /* ptr to putch() buffer */

    unsigned short _ch_buf_used;   /* if the _con_ch_buf is used */

    /* following fields are needed by _beginthread code */

    void *      _initaddr;      /* initial user thread address */

    void *      _initarg;       /* initial user thread argument */

    /* following three fields are needed to support signal handling and

     * runtime errors */

    void *      _pxcptacttab;   /* ptr to exception-action table */

    void *      _tpxcptinfoptrs; /* ptr to exception info pointers */

    int         _tfpecode;      /* float point exception code */

    /* pointer to the copy of the multibyte character information used by

     * the thread */

    pthreadmbcinfo  ptmbcinfo;

    /* pointer to the copy of the locale informaton used by the thead */

    pthreadlocinfo  ptlocinfo;

    int         _ownlocale;     /* if 1, this thread owns its own locale */

    /* following field is needed by NLG routines */

    unsigned long   _NLG_dwCode;

    /*

     * Per-Thread data needed by C++ Exception Handling

     */

    void *      _terminate;     /* terminate() routine */

    void *      _unexpected;    /* unexpected() routine */

    void *      _translator;    /* S.E. translator */

    void *      _purecall;      /* called when pure virtual happens */

    void *      _curexception;  /* current exception */

    void *      _curcontext;    /* current exception context */

    int         _ProcessingThrow; /* for uncaught_exception */

    void *              _curexcspec;    /* for handling exceptions thrown from std::unexpected */

#if defined (_M_IA64) || defined (_M_AMD64)

    void *      _pExitContext;

    void *      _pUnwindContext;

    void *      _pFrameInfoChain;

    unsigned __int64    _ImageBase;

#if defined (_M_IA64)

    unsigned __int64    _TargetGp;

#endif  /* defined (_M_IA64) */

    unsigned __int64    _ThrowImageBase;

    void *      _pForeignException;

#elif defined (_M_IX86)

    void *      _pFrameInfoChain;

#endif  /* defined (_M_IX86) */

    _setloc_struct _setloc_data;

    void *      _encode_ptr;    /* EncodePointer() routine */

    void *      _decode_ptr;    /* DecodePointer() routine */

    void *      _reserved1;     /* nothing */

    void *      _reserved2;     /* nothing */

    void *      _reserved3;     /* nothing */

    int _cxxReThrow;        /* Set to True if it’s a rethrown C++ Exception */

    unsigned long __initDomain;     /* initial domain used by _beginthread[ex] for managed function */

};

typedef struct _tiddata * _ptiddata;

從上面的代碼可以看出很多內(nèi)容就是前面我們提到的C運(yùn)行庫(kù)不安全的內(nèi)容。
/***
*_threadstartex() - New thread begins here
*
*Purpose:
*       The new thread begins execution here.  This routine, in turn,
*       passes control to the user’s code.
*
*Entry:
*       void *ptd       = pointer to _tiddata structure for this thread
*
*Exit:
*       Never returns - terminates thread!
*
*Exceptions:
*
*******************************************************************************/
static unsigned long WINAPI _threadstartex (
        void * ptd
        )
{
        _ptiddata _ptd;                  /* pointer to per-thread data */
        /* Initialize FlsGetValue function pointer */
        __set_flsgetvalue();
        /*
         * Check if ptd is initialised during THREAD_ATTACH call to dll mains
         */
        if ( ( _ptd = (_ptiddata)__fls_getvalue(__get_flsindex())) == NULL)
        {
            /*
             * Stash the pointer to the per-thread data stucture in TLS
             */
            if ( !__fls_setvalue(__get_flsindex(), ptd) )
                ExitThread(GetLastError());
            /*
             * Set the thread ID field — parent thread cannot set it after
             * CreateThread() returns since the child thread might have run
             * to completion and already freed its per-thread data block!
             */
            ((_ptiddata) ptd)->_tid = GetCurrentThreadId();
        }
        else
        {
            _ptd->_initaddr = ((_ptiddata) ptd)->_initaddr;
            _ptd->_initarg =  ((_ptiddata) ptd)->_initarg;
            _ptd->_thandle =  ((_ptiddata) ptd)->_thandle;
#if defined (_M_CEE) || defined (MRTDLL)
            _ptd->__initDomain=((_ptiddata) ptd)->__initDomain;
#endif  /* defined (_M_CEE) || defined (MRTDLL) */
            _freefls(ptd);
            ptd = _ptd;
        }
        /*
         * Call fp initialization, if necessary
         */
#ifndef MRTDLL
#ifdef CRTDLL
        _fpclear();
#else  /* CRTDLL */
        if (_FPmtinit != NULL &&
            _IsNonwritableInCurrentImage((PBYTE)&_FPmtinit))
        {
            (*_FPmtinit)();
        }
#endif  /* CRTDLL */
#endif  /* MRTDLL */
#if defined (_M_CEE) || defined (MRTDLL)
        DWORD domain=0;
        if(!_getdomain(&domain))
        {
            ExitThread(0);
        }
        if(domain!=_ptd->__initDomain)
        {
            /* need to transition to caller’s domain and startup there*/
            ::msclr::call_in_appdomain(_ptd->__initDomain, _callthreadstartex);
            return 0L;
        }
#endif  /* defined (_M_CEE) || defined (MRTDLL) */
        _callthreadstartex();
        /*
         * Never executed!
         */
        return(0L);
}
這個(gè)函數(shù)的主要功能就是使用線(xiàn)程本地存儲(chǔ)將數(shù)據(jù)結(jié)構(gòu)tiddata和線(xiàn)程關(guān)聯(lián)起來(lái)。以及一個(gè)SEH。至此,我們知道了為什么C/C++運(yùn)行庫(kù)的 函數(shù)需要為為他創(chuàng)建的每個(gè)函數(shù)設(shè)置單獨(dú)的內(nèi)存塊,同時(shí)也了解了如何通過(guò)調(diào)用_beginthreadex函數(shù)來(lái)分配內(nèi)存塊以及初始化,并且還和線(xiàn)程關(guān)聯(lián)起 來(lái)。
        下面我們來(lái)看一下如果應(yīng)用程序直接調(diào)用CreateThread會(huì)如何。首先他肯定沒(méi)有創(chuàng)建與線(xiàn)程關(guān)聯(lián)的內(nèi)存塊,但是在運(yùn)行的過(guò)程中很有可能有其他的 C++運(yùn)行庫(kù)的函數(shù)企圖去獲得線(xiàn)程內(nèi)存塊(TlsGetValue)。結(jié)果當(dāng)然是返回NULL。這是C++運(yùn)行庫(kù)會(huì)自動(dòng)為線(xiàn)程創(chuàng)建一個(gè)內(nèi)存塊。看起來(lái)很完 美,系統(tǒng)想得真是周到。但是仍然有兩個(gè)問(wèn)題:1. 沒(méi)有SEH。程序可能直接掛掉;2.系統(tǒng)自動(dòng)創(chuàng)建的內(nèi)存塊誰(shuí)負(fù)責(zé)釋放呢?
         C++運(yùn)行庫(kù)還提供了另外一個(gè)函數(shù)創(chuàng)建線(xiàn)程:_beginthread。這個(gè)函數(shù)能用嗎?能用!但是不提倡。因?yàn)椋?.無(wú)法創(chuàng)建帶有安全屬性的線(xiàn)程;2.無(wú)法創(chuàng)建暫停的線(xiàn)程;3.無(wú)法返回線(xiàn)程ID。
         如何銷(xiāo)毀終止線(xiàn)程
          終止線(xiàn)程運(yùn)行有幾種方法:
1. 線(xiàn)程函數(shù)返回結(jié)束;
       這是線(xiàn)程結(jié)束的唯一的正確方法。線(xiàn)程函數(shù)必須設(shè)計(jì)成需要線(xiàn)程結(jié)束時(shí)就能返回的形式。通過(guò)這種方式結(jié)束線(xiàn)程可以保證:a。所有 C++對(duì)象正確的被銷(xiāo)毀;b。線(xiàn)程正確的釋放線(xiàn)程棧使用的內(nèi)存;c。系統(tǒng)將線(xiàn)程函數(shù)的返回值設(shè)置成線(xiàn)程的推出碼;d。系統(tǒng)遞減線(xiàn)程的計(jì)數(shù)器;
2.調(diào)用ExitThread;
        用這種方式終止線(xiàn)程能讓系統(tǒng)自動(dòng)清除所有線(xiàn)程使用的所有系統(tǒng)資源。但是無(wú)法清除C++資源。
3.其他線(xiàn)程調(diào)用TerminateThread;
        這種方法無(wú)法實(shí)現(xiàn)1中的a和b兩項(xiàng)。而且這個(gè)函數(shù)是異步函數(shù)。
4.創(chuàng)建線(xiàn)程的進(jìn)程終止;
        這種方法最野蠻,問(wèn)題也最多。1中的四項(xiàng)都無(wú)法保證。而且很有可能是線(xiàn)程要訪(fǎng)問(wèn)的資源不存在了,但是線(xiàn)程還在,這樣必然導(dǎo)致彈出出錯(cuò)對(duì)話(huà)框。而且內(nèi)存數(shù)據(jù)也不會(huì)存入硬盤(pán)。
線(xiàn)程終止過(guò)程:
1. 線(xiàn)程所有的用戶(hù)對(duì)象將被釋放,但是窗口和鉤子比較特殊,當(dāng)線(xiàn)程終止時(shí),所有窗口將被銷(xiāo)毀,同時(shí)所有的鉤子將被卸載。其他資源在進(jìn)程終止時(shí)才被銷(xiāo)毀。
2. 線(xiàn)程的退出代碼從STILL_ACTIVE改為ExitThread或TerminateThread的代碼。
3. 線(xiàn)程內(nèi)核對(duì)象的狀態(tài)變成已經(jīng)通知。
4. 如果線(xiàn)程是進(jìn)程的最后一個(gè)線(xiàn)程,那么該進(jìn)程也被視為已經(jīng)終止運(yùn)行。
5.線(xiàn)程內(nèi)核對(duì)象的使用計(jì)數(shù)遞減1.一個(gè)線(xiàn)程終止運(yùn)行時(shí),在線(xiàn)程的內(nèi)核對(duì)象所有相關(guān)聯(lián)的引用都關(guān)閉之前,該內(nèi)核對(duì)象不會(huì)被自動(dòng)釋放。
前面線(xiàn)程的創(chuàng)建中我們介紹了不用直接使用CreateThread而應(yīng)該使用C++運(yùn)行庫(kù)提供的函數(shù),VC提供的是 _beginthreadex,也不要使用_beginthread.那么線(xiàn)程結(jié)束也應(yīng)該使用C++運(yùn)行庫(kù)提供的函數(shù)。VC提供了兩個(gè)函數(shù) _endthreadex和_endthread.
void __cdecl _endthreadex(unsigned retcode)
{
_ptiddata ptd;
pid = _getptd();
__freeptd(ptd);
ExitThread(retcode)
}
從該函數(shù)的實(shí)現(xiàn)上看,我們知道他做了兩個(gè)事情,一個(gè)是釋放內(nèi)存塊。然后調(diào)用系統(tǒng)的ExitThread,真正推出線(xiàn)程。現(xiàn)在大家知道為什么前面 我們說(shuō)直接調(diào)用ExitThread會(huì)引起內(nèi)存塊的泄漏。因此如果想要強(qiáng)制終止線(xiàn)程可以調(diào)用_endthreadex而不是調(diào)用ExitThread。但 是一般不要調(diào)用。
        我們來(lái)看一下_endthread函數(shù)。他和_endthreadex有點(diǎn)不同。最大的區(qū)別在于它在調(diào)用ExitThread之前調(diào)用了 CloseHandle,如果這時(shí)候線(xiàn)程的計(jì)數(shù)器已經(jīng)為0,那么內(nèi)核對(duì)象將被釋放。此后所有其他線(xiàn)程調(diào)用此內(nèi)核對(duì)象都將失敗。
        最后我們來(lái)看一下,線(xiàn)程的標(biāo)識(shí)。標(biāo)識(shí)有兩個(gè),一個(gè)是句柄,一個(gè)是ID。句柄用得比較多??梢酝ㄟ^(guò)GetCurrentThread獲得當(dāng)前線(xiàn)程句柄。但是 要注意的是該句柄是一個(gè)偽句柄。線(xiàn)程偽句柄是一個(gè)特別的數(shù)(0xfffffffe),只是代表當(dāng)前的線(xiàn)程句柄(假如將某個(gè)線(xiàn)程的偽句柄拿到另一個(gè)線(xiàn)程去使 用,那么這個(gè)偽句柄實(shí)際操作另一個(gè)線(xiàn)程),因此它不會(huì)影響線(xiàn)程的引用計(jì)數(shù)。所以不需要調(diào)用CloseHandle。當(dāng)然調(diào)用了不會(huì)有問(wèn) 題,CloseHandle會(huì)忽略。線(xiàn)程的真實(shí)句柄:每個(gè)進(jìn)程有一張內(nèi)核對(duì)象表,這個(gè)表里放置進(jìn)程內(nèi)打開(kāi)的所有內(nèi)核對(duì)象,并給每個(gè)對(duì)象分配一個(gè)序號(hào),線(xiàn)程 句柄實(shí)際上就是 內(nèi)核對(duì)象表中對(duì)應(yīng)線(xiàn)程對(duì)象的序號(hào)。因此句柄與進(jìn)程相關(guān)。在這個(gè)進(jìn)程中的句柄在不能隨意拿到另一個(gè)進(jìn)程中使用,可以通過(guò)DuplicateHandle進(jìn)行 句柄拷貝。打開(kāi)一個(gè)句柄,會(huì)使線(xiàn)程對(duì)象的引用技術(shù)加一,CloseHandle會(huì)使線(xiàn)程對(duì)象引用計(jì)數(shù)減一,所以使用完句柄后需要進(jìn)行關(guān)閉。

    關(guān)于線(xiàn)程的實(shí)現(xiàn)細(xì)節(jié),我們這里只討論兩個(gè)問(wèn)題。即線(xiàn)程的內(nèi)核對(duì)象和線(xiàn)程棧以及線(xiàn)程的創(chuàng)建過(guò)程。上一章我們提到,線(xiàn)程是由線(xiàn)程內(nèi)核對(duì)象以及線(xiàn)程棧兩個(gè)部分組成的。系統(tǒng)通過(guò)線(xiàn)程的內(nèi)核對(duì)象管理線(xiàn)程。線(xiàn)程棧則維護(hù)參數(shù)和局部變量。

1. 線(xiàn)程的內(nèi)核對(duì)象和線(xiàn)程棧
         下表很清楚的表明了內(nèi)核對(duì)象和線(xiàn)程棧的內(nèi)容以及直接的管理。

調(diào)用了CreateThread之后,系統(tǒng)會(huì)創(chuàng)建線(xiàn)程的內(nèi)核對(duì)象。內(nèi)核對(duì)象包括一個(gè)上下文(用于恢復(fù)現(xiàn)場(chǎng))。從上圖可以看出。SP指向了線(xiàn)程棧的首 地址。IP指向了系統(tǒng)提供的一個(gè)函數(shù)。引用計(jì)數(shù)是表示當(dāng)前內(nèi)核對(duì)象的引用次數(shù),當(dāng)遞減到0,內(nèi)核對(duì)象將被刪除。掛起計(jì)數(shù)表示此內(nèi)核對(duì)象被掛起的計(jì)數(shù),當(dāng)減 到0時(shí),線(xiàn)程成為可調(diào)度狀態(tài)。剛剛創(chuàng)建的線(xiàn)程退出碼都是STILL_ACTIVE。都是沒(méi)有信號(hào)狀態(tài)。也許你問(wèn)一個(gè)問(wèn)題:為什么初始引用計(jì)數(shù)要等于2呢? 因?yàn)榫€(xiàn)程創(chuàng)建的時(shí)候要返回一個(gè)句柄,他擁有了一個(gè)計(jì)數(shù),另一個(gè)計(jì)數(shù)是被創(chuàng)建的新線(xiàn)程自己擁有的。在線(xiàn)程函數(shù)返回時(shí)遞減。弄清楚了這個(gè)我們也就清楚了在線(xiàn)程 函數(shù)最后返回前是否需要調(diào)用_endthread或_endthreadex或調(diào)用CloseHandle關(guān)閉自己。線(xiàn)程內(nèi)核對(duì)象只有在計(jì)數(shù)器減到0時(shí)才 會(huì)被系統(tǒng)銷(xiāo)毀。銷(xiāo)毀之后再調(diào)用與線(xiàn)程句柄相關(guān)的函數(shù)都可能出錯(cuò)。比如CloseHandle,當(dāng)然如果傳入的是偽句柄不一定,但是可能遞減了其他線(xiàn)程的計(jì) 數(shù)器。

       線(xiàn)程的內(nèi)核對(duì)象一旦創(chuàng)建完畢,系統(tǒng)就分配用于線(xiàn)程棧的內(nèi)存。內(nèi)存是用進(jìn)程的地址空間分配來(lái)的。然后,系統(tǒng)將兩個(gè)值寫(xiě)入棧的上端。第一個(gè)值是傳給 CreateThread的參數(shù)(最后傳入線(xiàn)程函數(shù)),另一個(gè)是線(xiàn)程函數(shù)的地址。從上圖可以看到,內(nèi)核對(duì)象初始化后,IP指向了一個(gè)系統(tǒng)的函數(shù) BaseThreadStart而不是用戶(hù)的線(xiàn)程函數(shù)。否則系統(tǒng)就無(wú)法在線(xiàn)程函數(shù)返回時(shí)調(diào)用ExitThread函數(shù)了。

1. 線(xiàn)程的創(chuàng)建過(guò)程

           a.建立線(xiàn)程內(nèi)核對(duì)象
           b.設(shè)置內(nèi)核對(duì)象初始值:比如計(jì)數(shù)器=2等
           c.分配內(nèi)存給線(xiàn)程棧
           d.初始化線(xiàn)程棧:將線(xiàn)程函數(shù)和參數(shù)push
           e.初始化上下文內(nèi)容:比如IP和SP以及其他CPU寄存器
            f. 調(diào)用BaseThreadStart函數(shù)
            g.調(diào)用用戶(hù)線(xiàn)程函數(shù)


終于開(kāi)始寫(xiě)到3了,最近一直很忙,似乎還有點(diǎn)累。上班時(shí)間是不可能有空寫(xiě)的,只有回到家等女兒睡著了才能坐下來(lái)。每每這個(gè)時(shí)候也不早了,1:23,現(xiàn)在。后面還有好多,還得加油。

        說(shuō)到線(xiàn)程的調(diào)度,我覺(jué)得還是得從線(xiàn)程的優(yōu)先級(jí)講起。在Windows中,每個(gè)線(xiàn)程都被賦予了優(yōu)先級(jí)的概念,線(xiàn)程總共有32個(gè)優(yōu)先級(jí)數(shù)從0(最低)到 31(最高)。從前面的章節(jié)我們知道,線(xiàn)程是從屬于某個(gè)進(jìn)程的,那么對(duì)于某個(gè)具體進(jìn)程的某個(gè)線(xiàn)程的優(yōu)先級(jí)是怎么確定的呢?這得從幾個(gè)優(yōu)先級(jí)概念說(shuō)起。一個(gè) 線(xiàn)程的優(yōu)先級(jí)是由進(jìn)程的基本優(yōu)先級(jí)(或者叫優(yōu)先級(jí)類(lèi))和線(xiàn)程的相對(duì)優(yōu)先級(jí)決定的。因?yàn)槲覀儫o(wú)法直接設(shè)置一個(gè)線(xiàn)程的優(yōu)先級(jí)數(shù),比如2或29。
        進(jìn)程的基本優(yōu)先級(jí):他是通過(guò)調(diào)用系統(tǒng)API—SetPriorityClass來(lái)設(shè)置,運(yùn)行中的進(jìn)程的優(yōu)先級(jí)可以通過(guò)任務(wù)管理器查看?;緝?yōu)先級(jí)是針對(duì)進(jìn)程來(lái)講的,基本優(yōu)先級(jí)有8個(gè)類(lèi)別,分別是:ABOVE_NORMAL_PRIORITY_CLASS,
BELOW_NORMAL_PRIORITY_CLASS,
HIGH_PRIORITY_CLASS,IDLE_PRIORITY_CLASS,
NORMAL_PRIORITY_CLASS,
PROCESS_MODE_BACKGROUND_BEGIN,
PROCESS_MODE_BACKGROUND_END,
REALTIME_PRIORITY_CLASS.
具體的含義可以查看MSDN。如果程序沒(méi)有進(jìn)行特別設(shè)置,那么進(jìn)程的基本優(yōu)先級(jí)將會(huì)設(shè)置為NORMAL_PRIORITY_CLASS這個(gè)級(jí)別。先對(duì)來(lái) 說(shuō),使用高于正常的優(yōu)先級(jí)需要特別小心,特別是REALTIME_PRIORITY_CLASS.他表示進(jìn)程中線(xiàn)程必須立即對(duì)事件作出響應(yīng),以便執(zhí)行關(guān)鍵 時(shí)間的任務(wù).該進(jìn)程中的線(xiàn)程還會(huì)搶先于操作系統(tǒng)組件之前運(yùn)行.使用本優(yōu)先級(jí)類(lèi)時(shí)必須極端小心.
線(xiàn)程相對(duì)優(yōu)先級(jí):相對(duì)優(yōu)先級(jí)是針對(duì)線(xiàn)程的,他是通過(guò)調(diào)用SetThreadPriority函數(shù)設(shè)置的。具體參數(shù)可以 參考MSDN.那么線(xiàn)程的優(yōu)先級(jí)到底是怎么確定的呢?基本優(yōu)先級(jí)和相對(duì)優(yōu)先級(jí)如何結(jié)合的呢?下表就是他們的對(duì)應(yīng)關(guān)系,是從MSDN上copy過(guò)來(lái)的。不過(guò) M$也說(shuō)過(guò)他們不承諾任何關(guān)于優(yōu)先級(jí)級(jí)數(shù)的保證。因?yàn)檫@個(gè)未來(lái)可能會(huì)變的。所以寫(xiě)程序千萬(wàn)不要直接依賴(lài)于有限級(jí)數(shù)。
具體的含義可以查看MSDN。如果程序沒(méi)有進(jìn)行特別設(shè)置,那么進(jìn)程的基本優(yōu)先級(jí)將會(huì)設(shè)置為NORMAL_PRIORITY_CLASS這個(gè)級(jí)別。先對(duì)來(lái) 說(shuō),使用高于正常的優(yōu)先級(jí)需要特別小心,特別是REALTIME_PRIORITY_CLASS.他表示進(jìn)程中線(xiàn)程必須立即對(duì)事件作出響應(yīng),以便執(zhí)行關(guān)鍵 時(shí)間的任務(wù).該進(jìn)程中的線(xiàn)程還會(huì)搶先于操作系統(tǒng)組件之前運(yùn)行.使用本優(yōu)先級(jí)類(lèi)時(shí)必須極端小心.
線(xiàn)程相對(duì)優(yōu)先級(jí):相對(duì)優(yōu)先級(jí)是針對(duì)線(xiàn)程的,他是通過(guò)調(diào)用SetThreadPriority函數(shù)設(shè)置的。具體參數(shù)可以 參考MSDN.那么線(xiàn)程的優(yōu)先級(jí)到底是怎么確定的呢?基本優(yōu)先級(jí)和相對(duì)優(yōu)先級(jí)如何結(jié)合的呢?下表就是他們的對(duì)應(yīng)關(guān)系,是從MSDN上copy過(guò)來(lái)的。不過(guò) M$也說(shuō)過(guò)他們不承諾任何關(guān)于優(yōu)先級(jí)級(jí)數(shù)的保證。因?yàn)檫@個(gè)未來(lái)可能會(huì)變的。所以寫(xiě)程序千萬(wàn)不要直接依賴(lài)于有限級(jí)數(shù)。

Process priority classThread priority levelBase priority
IDLE_PRIORITY_CLASSTHREAD_PRIORITY_IDLE1
THREAD_PRIORITY_LOWEST2
THREAD_PRIORITY_BELOW_NORMAL3
THREAD_PRIORITY_NORMAL4
THREAD_PRIORITY_ABOVE_NORMAL5
THREAD_PRIORITY_HIGHEST6
THREAD_PRIORITY_TIME_CRITICAL15
BELOW_NORMAL_PRIORITY_CLASSTHREAD_PRIORITY_IDLE1
THREAD_PRIORITY_LOWEST4
THREAD_PRIORITY_BELOW_NORMAL5
THREAD_PRIORITY_NORMAL6
THREAD_PRIORITY_ABOVE_NORMAL7
THREAD_PRIORITY_HIGHEST8
THREAD_PRIORITY_TIME_CRITICAL15
NORMAL_PRIORITY_CLASSTHREAD_PRIORITY_IDLE1
THREAD_PRIORITY_LOWEST6
THREAD_PRIORITY_BELOW_NORMAL7
THREAD_PRIORITY_NORMAL8
THREAD_PRIORITY_ABOVE_NORMAL9
THREAD_PRIORITY_HIGHEST10
THREAD_PRIORITY_TIME_CRITICAL15
ABOVE_NORMAL_PRIORITY_CLASSTHREAD_PRIORITY_IDLE1
THREAD_PRIORITY_LOWEST8
THREAD_PRIORITY_BELOW_NORMAL9
THREAD_PRIORITY_NORMAL10
THREAD_PRIORITY_ABOVE_NORMAL11
THREAD_PRIORITY_HIGHEST12
THREAD_PRIORITY_TIME_CRITICAL15
HIGH_PRIORITY_CLASSTHREAD_PRIORITY_IDLE1
THREAD_PRIORITY_LOWEST11
THREAD_PRIORITY_BELOW_NORMAL12
THREAD_PRIORITY_NORMAL13
THREAD_PRIORITY_ABOVE_NORMAL14
THREAD_PRIORITY_HIGHEST15
THREAD_PRIORITY_TIME_CRITICAL15
REALTIME_PRIORITY_CLASSTHREAD_PRIORITY_IDLE16
THREAD_PRIORITY_LOWEST22
THREAD_PRIORITY_BELOW_NORMAL23
THREAD_PRIORITY_NORMAL24
THREAD_PRIORITY_ABOVE_NORMAL25
THREAD_PRIORITY_HIGHEST26
THREAD_PRIORITY_TIME_CRITICAL31

比如兩個(gè)優(yōu)先級(jí)都是正常的級(jí)別,那么線(xiàn)程的優(yōu)先級(jí)就是8。再如基本優(yōu)先級(jí)是REALTIME_PRIORITY_CLASS ,|
相對(duì)優(yōu)先級(jí)是THREAD_PRIORITY_TIME_CRITICAL,那么線(xiàn)程的優(yōu)先級(jí)將是31。
       如果你夠細(xì)心的話(huà),你也許發(fā)現(xiàn)了一個(gè)問(wèn)題,就是前面說(shuō)到線(xiàn)程的優(yōu)先級(jí)是從0到31的,但是從表中來(lái)看并沒(méi)有那種組合的結(jié)果優(yōu)先級(jí)是0.沒(méi)錯(cuò),真的沒(méi)有0, 確實(shí)是組合不錯(cuò)來(lái)的。因?yàn)楫?dāng)系統(tǒng)引導(dǎo)的時(shí)候,它會(huì)創(chuàng)建一個(gè)特殊的線(xiàn)程,稱(chēng)為0頁(yè)線(xiàn)程.該線(xiàn)程被賦予優(yōu)先級(jí)0,它是整個(gè)系統(tǒng)中唯一的一個(gè)在優(yōu)先級(jí)0上運(yùn)行的線(xiàn)程 。 當(dāng)系統(tǒng)中沒(méi)有任何線(xiàn)程需要執(zhí)行的時(shí)候,0頁(yè)線(xiàn)程負(fù)責(zé)將系統(tǒng)中的所有空閑的RAM頁(yè)面置0.除了0還有其他一些數(shù)字在上面表里也找不到,比如 17,18,19,20,21,27,28,29,30都沒(méi)有。17,18,19,20,21,27,28,29,30。如果編寫(xiě)一個(gè)以?xún)?nèi)核方式運(yùn)行的設(shè) 備驅(qū)動(dòng)程序,可以獲得這些優(yōu)先級(jí)的等級(jí),而用戶(hù)方式的應(yīng)用程序則不能.
        說(shuō)到了線(xiàn)程優(yōu)先級(jí),不得不說(shuō)一下硬件中斷。我們知道不同的處理器的中斷機(jī)制是不一樣的,雖然中斷控制器做了中斷級(jí)別的優(yōu)化工作,Windows還是強(qiáng)制使 用自己的中斷優(yōu)先級(jí)方案,即IRQLs。內(nèi)核在內(nèi)部以數(shù)字表示IRQLs,x86上是從 0到31,x64和IA64上是從0到15,數(shù)字越高則中斷的優(yōu)先級(jí)別越高。內(nèi)核為軟件中斷定義了一系列標(biāo)準(zhǔn)的IRQLs,HAL也將硬件診斷號(hào)映射到 IRQLs。

       中斷是根據(jù)優(yōu)先級(jí)被響應(yīng)的,一個(gè)高優(yōu)先級(jí)的中斷會(huì)比一個(gè)低優(yōu)先級(jí)的中斷先被響應(yīng)。當(dāng)有一個(gè)高優(yōu)先級(jí)的中斷產(chǎn)生,處理器保存被中斷線(xiàn)程的狀態(tài)并呼叫與之相關(guān) 的陷阱調(diào)度器(trap dispatchers)。陷阱調(diào)度器提升IRQL并調(diào)用該中斷的服務(wù)例程。在中斷例程結(jié)束之后,陷阱調(diào)度器降低處理器的IRQL到中斷發(fā)生前的級(jí)別,接 著載入之前存儲(chǔ)的機(jī)器狀態(tài)。被中斷的線(xiàn)程從被中斷的點(diǎn)重新開(kāi)始執(zhí)行。當(dāng)內(nèi)核降低IRQL后,之前被屏蔽的優(yōu)先級(jí)較低的中斷就可能被接收。如果順利的話(huà),內(nèi) 核會(huì)重復(fù)這一過(guò)程來(lái)處理新的中斷。
   每個(gè)處理器的IRQL設(shè)置決定了該處理器可接收的中斷。IRQLs也被用來(lái)同步訪(fǎng)問(wèn)內(nèi)核模式的數(shù)據(jù)結(jié)構(gòu)。當(dāng)一個(gè)線(xiàn)程在內(nèi)核模式下運(yùn)行,它既可以直接調(diào)用 KeRaiseIrql和KeLowerIrql來(lái)提升或降低處理器的IRQL,也可以間接地通過(guò)調(diào)用函數(shù)獲得內(nèi)核同步物件以提升或降低處理器的 IRQL。從擁有高于當(dāng)前IRQL的中斷源發(fā)出的中斷可以中斷處理器,而從有著小于或等于當(dāng)前IRQL的中斷源發(fā)出的中斷會(huì)被屏蔽,直到有執(zhí)行線(xiàn)程降低該 IRQL。
       一個(gè)內(nèi)核模式的線(xiàn)程提升或降低運(yùn)行它的處理器的IRQL取決于它的任務(wù)。例如,如果一個(gè)中斷發(fā)生,陷阱處理程序(或者是處理器)會(huì)提升處理器的IRQL到 為該中斷源指定的IRQL。這個(gè)高度屏蔽了所有小于或等于該IRQL的中斷(只作用于該處理器),保證了在處理器響應(yīng)該中斷的過(guò)程中不會(huì)被同級(jí)或級(jí)別較低 的中斷打斷。被屏蔽的中斷要么被交由其它的處理器處理,要么就要等到IRQL降下來(lái)。因此,系統(tǒng)中的所有組件,包括內(nèi)核和設(shè)備驅(qū)動(dòng)都希望IRQL能夠待在 無(wú)源級(jí)別(也叫作低級(jí)別)。原因是如果IRQL不被長(zhǎng)時(shí)間的置于較高的狀態(tài),設(shè)備驅(qū)動(dòng)就可以及時(shí)地響應(yīng)硬件中斷。
       IRQL的優(yōu)先級(jí)與線(xiàn)程調(diào)度的優(yōu)先級(jí)完全不同。后者是線(xiàn)程的一個(gè)屬性,而IRQL是中斷源,比如鍵盤(pán)、鼠標(biāo)的一個(gè)屬性。此外,每個(gè)處理器有一個(gè)IRQL設(shè) 置,該設(shè)置在執(zhí)行系統(tǒng)代碼時(shí)發(fā)生變化。所有的線(xiàn)程都運(yùn)行在中斷優(yōu)先級(jí)0和1上。內(nèi)核態(tài)的異步調(diào)用運(yùn)行在1上,用戶(hù)線(xiàn)程運(yùn)行在0上,內(nèi)核線(xiàn)程可以中斷用戶(hù)線(xiàn) 程。而且只有內(nèi)核線(xiàn)程才能提高自己的終端優(yōu)先級(jí)。用戶(hù)線(xiàn)程雖然提高優(yōu)先級(jí)可以阻塞系統(tǒng)線(xiàn)程,但是用戶(hù)線(xiàn)程優(yōu)先級(jí)的提高并不會(huì)阻塞硬件中斷。而線(xiàn)程調(diào)度代碼運(yùn)行在DPC/線(xiàn)程調(diào)度中斷優(yōu)先級(jí)(2級(jí))。這樣可防止調(diào)度器代碼與線(xiàn)程在訪(fǎng)問(wèn)調(diào)度器數(shù)據(jù)結(jié)構(gòu)時(shí)發(fā)生沖突。
        在了解了什么是優(yōu)先級(jí)以及如何設(shè)置優(yōu)先級(jí)和中斷之后,我們來(lái)了解另外一個(gè)概念—–時(shí)間配額。
        時(shí)間配額是一個(gè)線(xiàn)程從進(jìn)入運(yùn)行狀態(tài)到Windows檢查是否有其他優(yōu)先級(jí)相同的線(xiàn)程需要開(kāi)始運(yùn)行之間的時(shí)間總和。每個(gè)線(xiàn)程都有一個(gè)代表本 次運(yùn)行最大時(shí)間長(zhǎng)度的時(shí)間配額。時(shí)間配額不是一個(gè)時(shí)間長(zhǎng)度值,而一個(gè)稱(chēng)為配額單位(quantum unit)的整數(shù)。 缺省時(shí),在Windows 2000專(zhuān)業(yè)版中線(xiàn)程時(shí)間配額為6;而在Windows 2000服務(wù)器中線(xiàn)程時(shí)間配額為36。注冊(cè)表項(xiàng):HKEY_LOCAL_MACHINE\System\CurrentControlSet \Control\PriorityControl\Win32PrioritySeparation Win32PrioritySeparation 可以修改時(shí)間配額。
線(xiàn)程狀態(tài)

        線(xiàn)程在不同的時(shí)間有不同的線(xiàn)程狀態(tài),在NT內(nèi)核的windows中線(xiàn)程總共有7種狀態(tài):

線(xiàn)程狀態(tài)

說(shuō)明

Ready(就緒)

此狀態(tài)下的線(xiàn)程正在等待執(zhí)行,當(dāng)調(diào)度程序需要找一個(gè)線(xiàn)程來(lái)執(zhí)行時(shí),它僅考慮就緒狀態(tài)下的線(xiàn)程池。

Standby(備用)

已經(jīng)被選中(當(dāng)前活動(dòng)線(xiàn)程的后繼),當(dāng)條件合適時(shí),調(diào)度程序?qū)@個(gè)線(xiàn)程執(zhí)行一個(gè)上下文轉(zhuǎn)換,備用線(xiàn)程將被切換到某個(gè)特定的處理器上運(yùn)行。對(duì)于系統(tǒng)中的每一個(gè)處理器,只能有一個(gè)線(xiàn)程處于備用狀態(tài)。

Running(運(yùn)行)

一旦調(diào)度程序?qū)h(huán)境切換到某個(gè)(備用)線(xiàn)程,這個(gè)線(xiàn)程就進(jìn)入運(yùn)行狀態(tài)并開(kāi)始執(zhí)行。線(xiàn)程一直執(zhí)行,直到內(nèi)核將其搶占去運(yùn)行一個(gè)更高優(yōu)先級(jí)的線(xiàn)程,或者它的時(shí)間片到結(jié)束運(yùn)行或自動(dòng)進(jìn)入等待狀態(tài)。

Waiting(等待)

一個(gè)線(xiàn)程可能因?yàn)橐韵聨讉€(gè)原因而進(jìn)入等待狀態(tài):(1)自動(dòng)等待一個(gè)對(duì)象以便同步它的執(zhí)行。(2)操作系統(tǒng)可以代替該進(jìn)程進(jìn)入等待(如為了解決換頁(yè)I/O)。(3)環(huán)境子系統(tǒng)引導(dǎo)線(xiàn)程掛起。

線(xiàn)程等待狀態(tài)結(jié)束后,根據(jù)其優(yōu)先級(jí),開(kāi)始執(zhí)行,或者進(jìn)入就緒狀態(tài)。

Transition (轉(zhuǎn)變)

當(dāng)一個(gè)線(xiàn)程已經(jīng)準(zhǔn)備好執(zhí)行,但它的內(nèi)核棧被換出了內(nèi)存,這時(shí)線(xiàn)程就進(jìn)入轉(zhuǎn)變狀態(tài)。一旦它的內(nèi)核棧被換入內(nèi)存,線(xiàn)程就進(jìn)入就緒狀態(tài)。

Terminated (終止)

當(dāng)一個(gè)線(xiàn)程完成執(zhí)行,它就進(jìn)入終止?fàn)顟B(tài)。終止后,線(xiàn)程對(duì)象可能被刪除,也可能不被刪除,這將取決于對(duì)象管理器什么時(shí)候刪除對(duì)象的策略。如果執(zhí)行體中有一個(gè)指針指向線(xiàn)程對(duì)象,執(zhí)行體可以對(duì)線(xiàn)程對(duì)象重新初始化并再次使用它。

Initialized (初始)

當(dāng)一個(gè)線(xiàn)程被創(chuàng)建時(shí)的狀態(tài)。(內(nèi)部使用)

下面這個(gè)圖是各個(gè)狀態(tài)之間的轉(zhuǎn)換關(guān)系:

處理器的親合性
        按照系統(tǒng)默認(rèn)設(shè)置,當(dāng)系統(tǒng)將CPU分配給線(xiàn)程時(shí),如果其他因素都相同的話(huà),那么系統(tǒng)將設(shè)法在線(xiàn)程上次運(yùn)行的那個(gè)處理器上再次運(yùn)行線(xiàn)程。這樣是為了重復(fù)使用CPU的高速緩存。這就是系統(tǒng)的軟親合性。但是為了適應(yīng)某些特殊情況,系統(tǒng)將允許用戶(hù)設(shè)置線(xiàn)程或進(jìn)程的親合性。也就是允許哪個(gè)CPU能允許哪些線(xiàn)程。這就是處理器的硬親合性。API為:SetProcessAffinityMask/SetThreadAffinityMask。
線(xiàn)程調(diào)度
        Windows是一個(gè)基于優(yōu)先級(jí)的搶占式多處理器調(diào)度系統(tǒng)。調(diào)度系統(tǒng)總是運(yùn)行優(yōu)先級(jí)最高的就緒線(xiàn)程。Windows的沒(méi)有單獨(dú)的調(diào)度模塊或程序,調(diào)度的代 碼是在內(nèi)核中實(shí)現(xiàn)的,廣泛分布在內(nèi)核中那些與調(diào)度相關(guān)的事件發(fā)生的地方。這些負(fù)責(zé)調(diào)度的程序被總稱(chēng)為“內(nèi)核的調(diào)度器”。線(xiàn)程調(diào)度發(fā)生在 DPC/Dispatch級(jí)別。  當(dāng)一個(gè)線(xiàn)程進(jìn)入運(yùn)行狀態(tài)時(shí),每次時(shí)鐘中斷都會(huì)從時(shí)間配額中減少一個(gè)固定值(一般是3)。當(dāng)配額用完后,系統(tǒng)中斷線(xiàn)程, 看是否需要降低線(xiàn)程優(yōu)先級(jí),并查找是否有其他高優(yōu)先級(jí)或相同優(yōu)先級(jí)的線(xiàn)程在等待運(yùn)行。因?yàn)閣indows是一個(gè)搶占式的操作系統(tǒng),,因?yàn)橐粋€(gè)線(xiàn)程可能會(huì)在 自己時(shí)間配額還沒(méi)用完的時(shí)候就被其他已經(jīng)就緒的高優(yōu)先級(jí)的線(xiàn)程給搶先了。而且用戶(hù)線(xiàn)程可以搶先內(nèi)核線(xiàn)程。搶先時(shí)只與優(yōu)先級(jí)有關(guān)而不關(guān)心是用戶(hù)線(xiàn)程還是內(nèi)核 線(xiàn)程。處于實(shí)時(shí)優(yōu)先級(jí)的線(xiàn)程被搶先時(shí),時(shí)間配額被重置為一個(gè)完整的時(shí)間片,而處于動(dòng)態(tài)優(yōu)先級(jí)的線(xiàn)程被搶先時(shí),時(shí)間配額不變。當(dāng)一個(gè)線(xiàn)程出現(xiàn)等待事件時(shí),時(shí) 間配額會(huì)被減1,當(dāng)線(xiàn)程優(yōu)先級(jí)大于14時(shí),優(yōu)先級(jí)被重置。有四種情況會(huì)引起線(xiàn)程的調(diào)度: 

  • 變成就緒狀態(tài)的線(xiàn)程。例如:一個(gè)新創(chuàng)建的線(xiàn)程,或者從等待狀態(tài)釋放出來(lái)的線(xiàn)程。

  • 因其時(shí)間配額用完而離開(kāi)運(yùn)行狀態(tài)的線(xiàn)程,它或者結(jié)束了,或者進(jìn)入等待狀態(tài)。

  • 線(xiàn)程的優(yōu)先級(jí)改變了,是因?yàn)橄到y(tǒng)調(diào)用,或者是Windows自己改變了優(yōu)先級(jí)。

  • 正在運(yùn)行的線(xiàn)程的處理器親合性改變了。

在每一個(gè)上述情況的銜接點(diǎn),Windows必須決定下一個(gè)運(yùn)行的線(xiàn)程是哪一個(gè)。一旦選擇了一個(gè)新的線(xiàn)程運(yùn)行,Windows將對(duì)其執(zhí)行一個(gè)上下文轉(zhuǎn)換的操作,即保存正在運(yùn)行的線(xiàn)程的相關(guān)的機(jī)器狀態(tài),裝載另一個(gè)線(xiàn)程的狀態(tài),開(kāi)始新線(xiàn)程的執(zhí)行。
動(dòng)態(tài)提高優(yōu)先級(jí)
        windows(2k/xp)在下面5種情況會(huì)自動(dòng)提高線(xiàn)程的優(yōu)先級(jí):

  • I/O操作完成后的線(xiàn)程優(yōu)先級(jí)提升    1. 在完成I/O操作后,Windows 2000將臨時(shí)提升等待該操作線(xiàn)程的優(yōu)先級(jí),以保證等待I/O操作的線(xiàn)程能有更多的機(jī)會(huì)立即開(kāi)始處理得到的結(jié)果 
        2. 為了避免I/O操作導(dǎo)致對(duì)某些線(xiàn)程的不公平偏好,在I/O操作完成后喚醒等待線(xiàn)程時(shí)將把該線(xiàn)程的時(shí)間配額減1
        3.線(xiàn)程優(yōu)先級(jí)的實(shí)際提升值是由設(shè)備驅(qū)動(dòng)程序決定的。與I/O操作相關(guān)的線(xiàn)程優(yōu)先級(jí)提升建議值在文件“Wdm.h”或“Ntddk.h”中。設(shè)備驅(qū)動(dòng)程序在完成I/O請(qǐng)求時(shí)通過(guò)內(nèi)核函數(shù)IoCompleteRequest來(lái)指定優(yōu)先級(jí)提升的幅度。
        4. 線(xiàn)程優(yōu)先級(jí)的提升幅度與I/O請(qǐng)求的響應(yīng)時(shí)間要求是一致的,響應(yīng)時(shí)間要求越高,優(yōu)先級(jí)提升幅度越大

  • 等待事件和信號(hào)量后的線(xiàn)程優(yōu)先級(jí)提升。    1. 當(dāng)一個(gè)等待執(zhí)行事件對(duì)象或信號(hào)量對(duì)象的線(xiàn)程完成等待后,它的優(yōu)先級(jí)將提升一個(gè)優(yōu)先級(jí)。 
        2. 阻塞于事件或信號(hào)量的線(xiàn)程得到的處理機(jī)時(shí)間比處理機(jī)繁忙型線(xiàn)程要少,這種提升可減少這種不平衡帶來(lái)的影響。
        3. SetEvent、PulseEvent或ReleaseSemaphore函數(shù)調(diào)用可導(dǎo)致事件對(duì)象或信號(hào)量對(duì)象等待的結(jié)束。
         4. 提升是以線(xiàn)程的基本優(yōu)先級(jí)為基點(diǎn)的,而不是線(xiàn)程的當(dāng)前優(yōu)先級(jí)。提升后的優(yōu)先級(jí)永遠(yuǎn)不會(huì)超過(guò)15。在等待結(jié)束時(shí),線(xiàn)程的時(shí)間配額被減1,并在提升后的優(yōu)先級(jí) 上執(zhí)行完剩余的時(shí)間配額;隨后降低1個(gè)優(yōu)先級(jí),運(yùn)行一個(gè)新的時(shí)間配額,直到優(yōu)先級(jí)降低到初始的基本優(yōu)先級(jí)。

  • 前臺(tái)線(xiàn)程在等待結(jié)束后的優(yōu)先級(jí)提升
         1. 對(duì)于前臺(tái)進(jìn)程中的線(xiàn)程,一個(gè)內(nèi)核對(duì)象上的等待操作完成時(shí),內(nèi)核函數(shù)KiUnwaitThread會(huì)提升線(xiàn)程的當(dāng)前優(yōu)先級(jí)(不是線(xiàn)程的基本優(yōu)先級(jí)),提升幅度為變量PsPrioritySeparation的值。

    2. 在前臺(tái)應(yīng)用完成它的等待操作時(shí)小幅提升它的優(yōu)先級(jí),以使它更有可能馬上進(jìn)入運(yùn)行狀態(tài),有效改進(jìn)前臺(tái)應(yīng)用的響應(yīng)時(shí)間特征。

    3. 用戶(hù)不能禁止這種優(yōu)先級(jí)提升,甚至是在用戶(hù)已利用Win32的函數(shù)SetThreadPriorityBoost禁止了其他的優(yōu)先級(jí)提升策略時(shí),也是如此。

  • 圖形用戶(hù)接口線(xiàn)程被喚醒后的優(yōu)先級(jí)提升。
           1. 擁有窗口的線(xiàn)程在被窗口活動(dòng)喚醒(如收到窗口消息)時(shí)將得到一個(gè)幅度為2的額外優(yōu)先級(jí)提升。

     2. 窗口系統(tǒng)(Win32k.sys)在調(diào)用函數(shù)KeSetEvent時(shí)實(shí)施這種優(yōu)先級(jí)提升,KeSetEvent函數(shù)調(diào)用設(shè)置一個(gè)事件,用于喚醒一個(gè)圖形用戶(hù)接口線(xiàn)程。
           3. 這種優(yōu)先級(jí)提升的原因是改進(jìn)交互應(yīng)用的響應(yīng)時(shí)間。

  • 對(duì)處理機(jī)饑餓線(xiàn)程的優(yōu)先級(jí)提升
           1. 系統(tǒng)線(xiàn)程“平衡集管理器(balance set manager)” 會(huì)每秒鐘檢查一次就緒隊(duì)列,是否存在一直在就緒隊(duì)列中排隊(duì)超過(guò)300個(gè)時(shí)鐘中斷間隔的線(xiàn)程。

     2. 如果找到這樣的線(xiàn)程,平衡集管理器將把該線(xiàn)程的優(yōu)先級(jí)提升到15,并分配給它一個(gè)長(zhǎng)度為正常值兩倍的時(shí)間配額;

    3. 當(dāng)被提升線(xiàn)程用完它的時(shí)間配額后,該線(xiàn)程的優(yōu)先級(jí)立即衰減到它原來(lái)的基本優(yōu)先級(jí)。

    Sleep 函數(shù)

    系統(tǒng)將在大約的指定毫秒數(shù)內(nèi)使線(xiàn)程不可調(diào)度。Windows不是個(gè)實(shí)時(shí)操作系統(tǒng)。雖然線(xiàn)程可能在規(guī)定的時(shí)間被喚醒,但是它能否做 到,取決于系統(tǒng)中還有什么操作正在進(jìn)行。可以調(diào)用Sleep,并且為dwMilliseconds參數(shù)傳遞INFINITE。這將告訴系統(tǒng)永遠(yuǎn)不要調(diào)度該 線(xiàn)程。這不是一件值得去做的事情。最好是讓線(xiàn)程退出,并還原它的堆棧和內(nèi)核對(duì)象??梢詫?傳遞給Sleep。這將告訴系統(tǒng),調(diào)用線(xiàn)程將釋放剩余的時(shí)間片, 并迫使系統(tǒng)調(diào)度另一個(gè)線(xiàn)程。但是,系統(tǒng)可以對(duì)剛剛調(diào)用Sleep的線(xiàn)程重新調(diào)度。如果不存在多個(gè)擁有相同優(yōu)先級(jí)的可調(diào)度線(xiàn)程,就會(huì)出現(xiàn)這種情況。那么我們 如何做到真正切換到另外一個(gè)線(xiàn)程呢?答案是可以調(diào)用SwitchtoThread函數(shù)。

    SwitchtoThread函數(shù)

    系統(tǒng)提供了SwitchToThread函數(shù)。當(dāng)調(diào)用這個(gè)函數(shù)的時(shí)候,系統(tǒng)要查看是否存在一個(gè)迫切需要CPU時(shí)間的線(xiàn)程。如果沒(méi) 有線(xiàn)程迫切需要CPU時(shí)間,SwitchToThread就會(huì)立即返回。如果存在一個(gè)迫切需要CPU時(shí)間的線(xiàn)程,SwitchToThread就對(duì)該線(xiàn)程 進(jìn)行調(diào)度(該線(xiàn)程的優(yōu)先級(jí)可能低于調(diào)用SwitchToThread的線(xiàn)程)。這個(gè)迫切需要CPU時(shí)間的線(xiàn)程可以運(yùn)行一個(gè)時(shí)間段,然后系統(tǒng)調(diào)度程序照常運(yùn) 行。該函數(shù)允許一個(gè)需要資源的線(xiàn)程強(qiáng)制另一個(gè)優(yōu)先級(jí)較低、而目前卻擁有該資源的線(xiàn)程放棄該資源。如果調(diào)用SwitchToThread函數(shù)時(shí)沒(méi)有其他線(xiàn)程 能夠運(yùn)行,那么該函數(shù)返回FALSE,否則返回一個(gè)非0值。調(diào)用SwitchToThread與調(diào)用Sleep是相似的。差別是 SwitchToThread允許優(yōu)先級(jí)較低的線(xiàn)程運(yùn)行;而即使有低優(yōu)先級(jí)線(xiàn)程迫切需要CPU時(shí)間,Sleep也能夠立即對(duì)調(diào)用線(xiàn)程重新進(jìn)行調(diào)度。

    對(duì)稱(chēng)多處理機(jī)系統(tǒng)上Windows 2000的線(xiàn)程調(diào)度

    每個(gè)線(xiàn)程在對(duì)應(yīng)的內(nèi)核線(xiàn)程控制塊中都保存著兩個(gè)處理器標(biāo)識(shí):
    首選處理器:線(xiàn)程運(yùn)行時(shí)的偏好處理器
    第二處理器:線(xiàn)程運(yùn)行的第二選擇處理器
    首選處理器是基于進(jìn)程控制塊的索引值來(lái)隨機(jī)選擇的。索引值在創(chuàng)建每個(gè)線(xiàn)程時(shí)遞增。線(xiàn)程一旦創(chuàng)建后,系統(tǒng)就不會(huì)修改線(xiàn)程的首選處理器設(shè)置,但是用戶(hù)可以通過(guò)SetThreadIdleProcessor來(lái)修改.

    當(dāng)線(xiàn)程進(jìn)入運(yùn)行狀態(tài)時(shí),Windows首先試圖調(diào)度該線(xiàn)程到一個(gè)空閑處理機(jī)上運(yùn)行。如果有多個(gè)空閑處理機(jī),線(xiàn)程調(diào)度器的調(diào)度順序?yàn)椋?/p>

    –線(xiàn)程的首選處理機(jī)

    –線(xiàn)程的第二處理機(jī)

    –當(dāng)前執(zhí)行處理機(jī)(即正在執(zhí)行調(diào)度器代碼的處理機(jī))。

    –如果這些處理機(jī)都不是空閑的,Windows將依據(jù)處理機(jī)標(biāo)識(shí)從高到低掃描系統(tǒng)中的空閑處理機(jī)狀態(tài),選擇找到的第一個(gè)空閑處理機(jī)。

    如果線(xiàn)程進(jìn)入就緒狀態(tài)時(shí),所有處理機(jī)都處于繁忙狀態(tài),Windows將檢查一個(gè)處于運(yùn)行狀態(tài)或備用狀態(tài)的線(xiàn)程,判斷它是否可搶先。檢查的順序如下:

    –線(xiàn)程的首選處理機(jī)

    –線(xiàn)程的第二處理機(jī)

    –如果這兩個(gè)處理機(jī)都不在線(xiàn)程的親合掩碼中,Windows將依據(jù)活動(dòng)處理機(jī)掩碼選擇線(xiàn)程可運(yùn)行的編號(hào)最大的處理機(jī)。

    Windows并不檢查所有處理機(jī)上的運(yùn)行線(xiàn)程和備用線(xiàn)程的優(yōu)先級(jí),而僅僅檢查一個(gè)被選中處理機(jī)上的運(yùn)行線(xiàn)程和備用線(xiàn)程的優(yōu)先級(jí)。

    如果在被選中的處理機(jī)上沒(méi)有線(xiàn)程可被搶先,則新線(xiàn)程放入相應(yīng)優(yōu)先級(jí)的就緒隊(duì)列,并等待調(diào)度執(zhí)行。為特定的處理機(jī)調(diào)度線(xiàn)程

    在多處理機(jī)系統(tǒng),Windows不能簡(jiǎn)單地從就緒隊(duì)列中取第一個(gè)線(xiàn)程,它要在親合掩碼限制下尋找一個(gè)滿(mǎn)足下列條件之一的線(xiàn)程。

    –線(xiàn)程的上一次運(yùn)行是在該處理機(jī)上;

    –線(xiàn)程的首選處理機(jī)是該處理機(jī);

    –處于就緒狀態(tài)的時(shí)間超過(guò)2個(gè)時(shí)間配額;

    –優(yōu)先級(jí)大于等于24;

    如果Windows不能找到滿(mǎn)足要求的線(xiàn)程,它將從就緒隊(duì)列的隊(duì)首取第一個(gè)線(xiàn)程進(jìn)入運(yùn)行狀態(tài)。最高優(yōu)先級(jí)就緒線(xiàn)程可能不處于運(yùn)行狀態(tài).有可能出現(xiàn)這種情況,一個(gè)比當(dāng)前正在運(yùn)行線(xiàn)程優(yōu)先級(jí)更高的線(xiàn)程處于就緒狀態(tài),但不能立即搶先當(dāng)前線(xiàn)程,進(jìn)入運(yùn)行狀態(tài)。

    空閑線(xiàn)程

    如果在一個(gè)處理機(jī)上沒(méi)有可運(yùn)行的線(xiàn)程,Windows會(huì)調(diào)度相應(yīng)處理機(jī)對(duì)應(yīng)的空閑線(xiàn)程。由于在多處理機(jī)系統(tǒng)中可能兩個(gè)處理機(jī)同時(shí) 運(yùn)行空閑線(xiàn)程,所以系統(tǒng)中的每個(gè)處理機(jī)都有一個(gè)對(duì)應(yīng)的空閑線(xiàn)程。Windows給空閑線(xiàn)程指定的線(xiàn)程優(yōu)先級(jí)為0,該空閑線(xiàn)程只在沒(méi)有其他線(xiàn)程要運(yùn)行時(shí)才運(yùn) 行。

    對(duì) 于多線(xiàn)程編程來(lái)說(shuō),最難得有兩點(diǎn):1. 線(xiàn)程的生命周期的管理;2. 線(xiàn)程同步。在前面幾節(jié)中我們討論了第一個(gè)問(wèn)題,現(xiàn)在我們來(lái)討論第二個(gè)問(wèn)題。到目前為止,我們創(chuàng)建線(xiàn)程都是相互獨(dú)立的。線(xiàn)程之間沒(méi)有任何瓜葛。因此他們都運(yùn) 行得很好。沒(méi)有任何問(wèn)題。性能也很高。這一切看起來(lái)都非常的美好。比起單線(xiàn)程來(lái)說(shuō)簡(jiǎn)直好得太多了。然而不幸的是在多線(xiàn)程編程中,多個(gè)線(xiàn)程都是相互獨(dú)立的事 情太少了。更多的是雖然一個(gè)線(xiàn)程處理一些事情,另一個(gè)線(xiàn)程處理另外一些事情,但是其中一些線(xiàn)程需要了解其他線(xiàn)程的處理結(jié)果或者需要在其處理完成之后才能進(jìn) 行。最簡(jiǎn)單的情況比如:一個(gè)線(xiàn)程通過(guò)計(jì)算,將計(jì)算的結(jié)果寫(xiě)入一個(gè)全局變量,另外一個(gè)線(xiàn)程則讀取全局變量進(jìn)行顯示。如果不采取任何同步措施,那么就無(wú)法保證 讀數(shù)據(jù)的線(xiàn)程讀到的數(shù)據(jù)時(shí)寫(xiě)數(shù)據(jù)線(xiàn)程寫(xiě)入之后的結(jié)果。為了讓線(xiàn)程了解其他線(xiàn)程結(jié)束之后的結(jié)果或者需要訪(fǎng)問(wèn)相同的資源并改變資源從而使得同一進(jìn)程中的多個(gè)線(xiàn) 程協(xié)調(diào)的工作就叫線(xiàn)程的同步。因此也就有兩種基本情況是需要使用線(xiàn)程同步的:1. 當(dāng)多個(gè)線(xiàn)程會(huì)訪(fǎng)問(wèn)同一個(gè)資源并且會(huì)改變資源;2. 一個(gè)線(xiàn)程需要了解其他一個(gè)或多個(gè)線(xiàn)程何時(shí)結(jié)束(或者說(shuō)是需要通知其他線(xiàn)程);打個(gè)比方說(shuō)也就是“兄弟,這東西我且用著那,您得等會(huì)?!薄按蟾?,我活干完 啦,你上吧?!币簿瓦@么個(gè)意味。

           要使用好多線(xiàn)程,線(xiàn)程同步是一定要過(guò)關(guān)的。線(xiàn)程同步是一個(gè)不小的話(huà)題,從大方面來(lái)說(shuō)可以分為兩大類(lèi),一類(lèi)是用戶(hù)模式同步對(duì)象,一類(lèi)是內(nèi)核模式同步對(duì)象。前 面章節(jié)我們說(shuō)過(guò),使用內(nèi)核對(duì)象是需要從用戶(hù)態(tài)轉(zhuǎn)入內(nèi)核態(tài)的,而這個(gè)切換是非?;ㄙM(fèi)時(shí)間的,大約是1000個(gè)時(shí)鐘周期。所以用戶(hù)模式的同步對(duì)象最大的特點(diǎn)是 效率高。但是也有缺點(diǎn):不能跨進(jìn)程。而內(nèi)核模式同步對(duì)象大概正好相反。內(nèi)核對(duì)象可是系統(tǒng)級(jí)的東東,所謂錢(qián)多好辦事,自然功能也就強(qiáng)多了。
     用戶(hù)模式同步對(duì)象包括原子訪(fǎng)問(wèn)和臨界區(qū);內(nèi)核模式同步對(duì)象包括事件,等待定時(shí)器,信號(hào)量,互斥量。下面我們來(lái)學(xué)習(xí)每個(gè)對(duì)象的具體用法。

           1. 原子訪(fǎng)問(wèn)
            一直都不是很理解為啥叫原子訪(fǎng)問(wèn)。ATOM(阿童木),也許和那個(gè)認(rèn)為原子是最小物質(zhì)的年代的理論有關(guān)吧。
    既然是最小的物質(zhì)了,自然是不可以再分了。所以可以理解為原子訪(fǎng)問(wèn)就是不可以再分割的操作。也就是不會(huì)被其他更高優(yōu)先級(jí)中斷搶先得操作。說(shuō)得通俗點(diǎn)就是系統(tǒng)能保證這個(gè)操作是一次性搞定的,中途絕不能休息?;ユi函數(shù)的家族十分的龐大,可以查看msdn(http://msdn.microsoft.com/en-us/library/ms683597(VS.85).aspx)以InterLocked開(kāi)始的函數(shù)都是戶(hù)數(shù)函數(shù)。使用互鎖函數(shù)的優(yōu)點(diǎn)是:他的速度要比其他的CriticalSection,Mutex,Event,Semaphore快很多。通常少于50個(gè)時(shí)鐘周期

    一般的互鎖函數(shù):

    Interlocked functionDescription
    InterlockedAddPerforms an atomic addition operation on the specified LONG values.
    InterlockedAdd64Performs an atomic addition operation on the specified LONGLONG values.
    InterlockedAddAcquirePerforms an atomic addition operation on the specified LONG values. The operation is performed with acquire memory access semantics.
    InterlockedAddAcquire64Performs an atomic addition operation on the specified LONGLONG values. The operation is performed with acquire memory access semantics.
    InterlockedAddReleasePerforms an atomic addition operation on the specified LONG values. The operation is performed with release memory access semantics.
    InterlockedAddRelease64Performs an atomic addition operation on the specified LONGLONG values. The operation is performed with release memory access semantics.
    InterlockedAndPerforms an atomic AND operation on the specified LONG values.
    InterlockedAndAcquirePerforms an atomic AND operation on the specified LONG values. The operation is performed with acquire memory access semantics.
    InterlockedAndReleasePerforms an atomic AND operation on the specified LONG values. The operation is performed with release memory access semantics.
    InterlockedAnd8Performs an atomic AND operation on the specified char values.
    InterlockedAnd8AcquirePerforms an atomic AND operation on the specified char values. The operation is performed with acquire memory access semantics.
    InterlockedAnd8ReleasePerforms an atomic AND operation on the specified char values. The operation is performed with release memory access semantics.
    InterlockedAnd16Performs an atomic AND operation on the specified SHORT values.
    InterlockedAnd16AcquirePerforms an atomic AND operation on the specified SHORT values. The operation is performed with acquire memory access semantics.
    InterlockedAnd16ReleasePerforms an atomic AND operation on the specified SHORT values. The operation is performed with release memory access semantics.
    InterlockedAnd64Performs an atomic AND operation on the specified LONGLONG values.
    InterlockedAnd64AcquirePerforms an atomic AND operation on the specified LONGLONG values. The operation is performed with acquire memory access semantics.
    InterlockedAnd64ReleasePerforms an atomic AND operation on the specified LONGLONG values. The operation is performed with release memory access semantics.
    InterlockedBitTestAndResetTests the specified bit of the specified LONG value and sets it to 0.
    InterlockedBitTestAndReset64Tests the specified bit of the specified LONG64 value and sets it to 0.
    InterlockedBitTestAndSetTests the specified bit of the specified LONG value and sets it to 1.
    InterlockedBitTestAndSet64Tests the specified bit of the specified LONG64 value and sets it to 1.
    InterlockedCompare64Exchange128Performs an atomic compare-and-exchange operation on the specified values. The function compares the specified 64-bit values and exchanges with the specified 128-bit value based on the outcome of the comparison.
    InterlockedCompare64ExchangeAcquire128Performs an atomic compare-and-exchange operation on the specified values. The function compares the specified 64-bit values and exchanges with the specified 128-bit value based on the outcome of the comparison. The operation is performed with acquire memory access semantics.
    InterlockedCompare64ExchangeRelease128Performs an atomic compare-and-exchange operation on the specified values. The function compares the specified 64-bit values and exchanges with the specified 128-bit value based on the outcome of the comparison. The operation is performed with release memory access semantics.
    InterlockedCompareExchangePerforms an atomic compare-and-exchange operation on the specified values. The function compares two specified 32-bit values and exchanges with another 32-bit value based on the outcome of the comparison.
    InterlockedCompareExchange64Performs an atomic compare-and-exchange operation on the specified values. The function compares two specified 64-bit values and exchanges with another 64-bit value based on the outcome of the comparison.
    InterlockedCompareExchangeAcquirePerforms an atomic compare-and-exchange operation on the specified values. The function compares two specified 32-bit values and exchanges with another 32-bit value based on the outcome of the comparison. The operation is performed with acquire memory access semantics.
    InterlockedCompareExchangeAcquire64Performs an atomic compare-and-exchange operation on the specified values. The function compares two specified 64-bit values and exchanges with another 64-bit value based on the outcome of the comparison. The exchange is performed with acquire memory access semantics.
    InterlockedCompareExchangePointerPerforms an atomic compare-and-exchange operation on the specified pointer values. The function compares two specified pointer values and exchanges with another pointer value based on the outcome of the comparison.
    InterlockedCompareExchangePointerAcquirePerforms an atomic compare-and-exchange operation on the specified pointer values. The function compares two specified pointer values and exchanges with another pointer value based on the outcome of the comparison. The operation is performed with acquire memory access semantics.
    InterlockedCompareExchangePointerReleasePerforms an atomic compare-and-exchange operation on the specified pointer values. The function compares two specified pointer values and exchanges with another pointer value based on the outcome of the comparison. The operation is performed with release memory access semantics.
    InterlockedCompareExchangeReleasePerforms an atomic compare-and-exchange operation on the specified values. The function compares two specified 32-bit values and exchanges with another 32-bit value based on the outcome of the comparison. The exchange is performed with release memory access semantics.
    InterlockedCompareExchangeRelease64Performs an atomic compare-and-exchange operation on the specified values. The function compares two specified 64-bit values and exchanges with another 64-bit value based on the outcome of the comparison. The exchange is performed with release memory access semantics.
    InterlockedDecrementDecrements (decreases by one) the value of the specified 32-bit variable as an atomic operation.
    InterlockedDecrement64Decrements (decreases by one) the value of the specified 64-bit variable as an atomic operation.
    InterlockedDecrementAcquireDecrements (decreases by one) the value of the specified 32-bit variable as an atomic operation. The operation is performed with acquire memory access semantics.
    InterlockedDecrementAcquire64Decrements (decreases by one) the value of the specified 64-bit variable as an atomic operation. The operation is performed with acquire memory access semantics.
    InterlockedDecrementReleaseDecrements (decreases by one) the value of the specified 32-bit variable as an atomic operation. The operation is performed with release memory access semantics.
    InterlockedDecrementRelease64Decrements (decreases by one) the value of the specified 64-bit variable as an atomic operation. The operation is performed with release memory access semantics.
    InterlockedExchangeSets a 32-bit variable to the specified value as an atomic operation.
    InterlockedExchange64Sets a 64-bit variable to the specified value as an atomic operation.
    InterlockedExchangeAcquireSets a 32-bit variable to the specified value as an atomic operation. The operation is performed with acquire memory access semantics.
    InterlockedExchangeAcquire64Sets a 32-bit variable to the specified value as an atomic operation. The operation is performed with acquire memory access semantics.
    InterlockedExchangeAddPerforms an atomic addition of two 32-bit values.
    InterlockedExchangeAdd64Performs an atomic addition of two 64-bit values.
    InterlockedExchangeAddAcquirePerforms an atomic addition of two 32-bit values. The operation is performed with acquire memory access semantics.
    InterlockedExchangeAddAcquire64Performs an atomic addition of two 64-bit values. The operation is performed with acquire memory access semantics.
    InterlockedExchangeAddReleasePerforms an atomic addition of two 32-bit values. The operation is performed with release memory access semantics.
    InterlockedExchangeAddRelease64Performs an atomic addition of two 64-bit values. The operation is performed with release memory access semantics.
    InterlockedExchangePointerAtomically exchanges a pair of pointer values.
    InterlockedExchangePointerAcquireAtomically exchanges a pair of pointer values. The operation is performed with acquire memory access semantics.
    InterlockedIncrementIncrements (increases by one) the value of the specified 32-bit variable as an atomic operation.
    InterlockedIncrement64Increments (increases by one) the value of the specified 64-bit variable as an atomic operation.
    InterlockedIncrementAcquireIncrements (increases by one) the value of the specified 32-bit variable as an atomic operation. The operation is performed using acquire memory access semantics.
    InterlockedIncrementAcquire64Increments (increases by one) the value of the specified 64-bit variable as an atomic operation. The operation is performed using acquire memory access semantics.
    InterlockedIncrementReleaseIncrements (increases by one) the value of the specified 32-bit variable as an atomic operation. The operation is performed using release memory access semantics.
    InterlockedIncrementRelease64Increments (increases by one) the value of the specified 64-bit variable as an atomic operation. The operation is performed using release memory access semantics.
    InterlockedOrPerforms an atomic OR operation on the specified LONG values.
    InterlockedOrAcquirePerforms an atomic OR operation on the specified LONG values. The operation is performed with acquire memory access semantics.
    InterlockedOrReleasePerforms an atomic OR operation on the specified LONG values. The operation is performed with release memory access semantics.
    InterlockedOr8Performs an atomic OR operation on the specified char values.
    InterlockedOr8AcquirePerforms an atomic OR operation on the specified char values. The operation is performed with acquire memory access semantics.
    InterlockedOr8ReleasePerforms an atomic OR operation on the specified char values. The operation is performed with release memory access semantics.
    InterlockedOr16Performs an atomic OR operation on the specified SHORT values.
    InterlockedOr16AcquirePerforms an atomic OR operation on the specified SHORT values. The operation is performed with acquire memory access semantics.
    InterlockedOr16ReleasePerforms an atomic OR operation on the specified SHORT values. The operation is performed with release memory access semantics.
    InterlockedOr64Performs an atomic OR operation on the specified LONGLONG values.
    InterlockedOr64AcquirePerforms an atomic OR operation on the specified LONGLONG values. The operation is performed with acquire memory access semantics.
    InterlockedOr64ReleasePerforms an atomic OR operation on the specified LONGLONG values. The operation is performed with release memory access semantics.
    InterlockedXorPerforms an atomic XOR operation on the specified LONG values.
    InterlockedXorAcquirePerforms an atomic XOR operation on the specified LONG values. The operation is performed with acquire memory access semantics.
    InterlockedXorReleasePerforms an atomic XOR operation on the specified LONG values. The operation is performed with release memory access semantics.
    InterlockedXor8Performs an atomic XOR operation on the specified char values.
    InterlockedXor8AcquirePerforms an atomic XOR operation on the specified char values. The operation is performed with acquire memory access semantics.
    InterlockedXor8ReleasePerforms an atomic XOR operation on the specified char values. The operation is performed with release memory access semantics.
    InterlockedXor16Performs an atomic XOR operation on the specified SHORT values.
    InterlockedXor16AcquirePerforms an atomic XOR operation on the specified SHORT values. The operation is performed with acquire memory access semantics.
    InterlockedXor16ReleasePerforms an atomic XOR operation on the specified SHORT values. The operation is performed with release memory access semantics.
    InterlockedXor64Performs an atomic XOR operation on the specified LONGLONG values.
    InterlockedXor64AcquirePerforms an atomic XOR operation on the specified LONGLONG values. The operation is performed with acquire memory access semantics.
    InterlockedXor64ReleasePerforms an atomic XOR operation on the specified LONGLONG values. The operation is performed with release memory access semantics.

    鏈表的互鎖函數(shù):

    Singly-linked list functionDescription
    InitializeSListHeadInitializes the head of a singly linked list.
    InterlockedFlushSListFlushes the entire list of items in a singly linked list.
    InterlockedPopEntrySListRemoves an item from the front of a singly linked list.
    InterlockedPushEntrySListInserts an item at the front of a singly linked list.
    QueryDepthSListRetrieves the number of entries in the specified singly linked list.
    RtlFirstEntrySListRetrieves the first entry in a singly linked list.
    RtlInitializeSListHeadInitializes the head of a singly linked list. Applications should call InitializeSListHead instead.
    RtlInterlockedFlushSListFlushes the entire list of items in a singly linked list. Applications should call InterlockedFlushSList instead.
    RtlInterlockedPopEntrySListRemoves an item from the front of a singly linked list. Applications should call InterlockedPopEntrySList instead.
    RtlInterlockedPushEntrySListInserts an item at the front of a singly linked list. Applications should call InterlockedPushEntrySList instead.
    RtlQueryDepthSListRetrieves the number of entries in the specified singly linked list. Applications should call QueryDepthSList instead.

    具體用法可以查看MSDN。在眾多的原子操作函數(shù)中,最常見(jiàn)的有以下幾個(gè):
    InterlockedExhangeAdd,InterlockedExchange,InterlockedCompareExchange,InterlockedIncrement和InterlockedDecrement。最后兩個(gè)在COM中應(yīng)該很熟悉。

    2. 臨界區(qū)
    臨界區(qū)是指一小段代碼,在執(zhí)行前必須獨(dú)占對(duì)某些資源的訪(fǎng)問(wèn)權(quán)。也就是以原子操作的方式來(lái)訪(fǎng)問(wèn)資源。換句話(huà)說(shuō)就是你用了別人就沒(méi)法用了。 要用怎么辦?等唄。等多久?先占得人說(shuō)了算!中國(guó)有句古話(huà)叫“占著茅坑不拉屎”。在這里用太合適了。茅坑就是我們說(shuō)的資源,想用茅坑的人就是線(xiàn)程啦。一個(gè) 家伙進(jìn)去了,同時(shí)把牌牌給翻成紅色,這時(shí)候再有人要進(jìn)去就得等了。只有里面那個(gè)家伙出來(lái)并且把牌牌翻成綠色的才能進(jìn)去。所以用臨界區(qū)得非常小心才行。如果 出來(lái)那家伙人走了,可是忘記把牌牌翻成綠色的話(huà),雖然人走了,不過(guò)坑就算是廢啦??墒强墒悄氵€在傻傻的等。如果有個(gè)家伙進(jìn)去了,可是忘記把牌牌翻成紅色 的,這時(shí)你到了門(mén)口一看是綠牌牌自然就進(jìn)去了。。。oh,My God!所以各位千萬(wàn)記得使用臨界區(qū)時(shí)一定記得進(jìn)去出來(lái)都得“翻牌牌”。切記切記!
              臨界區(qū)是用戶(hù)態(tài)的同步對(duì)象,因此只能在進(jìn)程內(nèi)使用,他無(wú)法跨進(jìn)程。在使用的過(guò)程中也無(wú)法移動(dòng)或拷貝或修改。對(duì)于線(xiàn)程的獲取順序沒(méi)有定義,因此不能假定哪個(gè) 線(xiàn)程將會(huì)首先獲得臨界區(qū)。使用完臨界區(qū)必須執(zhí)行刪除操作。而且一個(gè)臨界區(qū)是不允許進(jìn)行重復(fù)初始化的。否則將產(chǎn)生不可預(yù)料的結(jié)果。
           臨界區(qū)的使用也很簡(jiǎn)單,主要有四個(gè)函數(shù):

       void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);初始化臨界區(qū)。這個(gè)函數(shù)雖然沒(méi)有返回值,但是在低內(nèi)存時(shí)會(huì)拋出STATUS_NO_MEMORY 的異常.

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多