• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            大龍的博客

            常用鏈接

            統(tǒng)計(jì)

            最新評(píng)論

            Windows線程函數(shù)概述 - 《C++編程藝術(shù)》

            Windows提供了多組支持多線程的應(yīng)用程序接口(API)函數(shù)。許多讀者已經(jīng)對(duì)Windows提供的多線程函數(shù)有一定程度的了解,但是對(duì)于那些不熟悉這些的讀者,本章提供了這些函數(shù)的概述。記住,Windows提供了許多其他的基于多線程的函數(shù),這些函數(shù)需要您自己去探索。

            為了使用Windows的多線程函數(shù),必須在程序中包含<Windows.h>。

            3.4.1 線程的創(chuàng)建和終止

            Windows API提供了CreateThread()函數(shù)來(lái)創(chuàng)建一個(gè)線程。其原型如下所示:

            HANDLE CreateThread(LPSECURITY_ATTRIBUTES secAttr,

            SIZE_T stackSize,

            LPTHREAD_START_ROUTINE threadFunc,

            LPVOID param,

            DWORD flags,

            LPDWORD threadID);

            在此,secAttr是一個(gè)用來(lái)描述線程的安全屬性的指針。如果secAttr是NULL,就會(huì)使用默認(rèn)的安全描述符。

            每個(gè)線程都具有自己的堆棧。可以使用stackSize參數(shù)來(lái)按字節(jié)指定新線程堆棧的大小。如果這個(gè)整數(shù)值為0,那么這個(gè)線程堆棧的大小與創(chuàng)建它的線程相同。如果需要的話,這個(gè)堆棧可以擴(kuò)展。(通常使用0來(lái)指定線程堆棧的大小)。

            每個(gè)線程都在創(chuàng)建它的進(jìn)程中通過(guò)調(diào)用線程函數(shù)來(lái)開(kāi)始執(zhí)行。線程的執(zhí)行一直持續(xù)到線程函數(shù)返回。這個(gè)函數(shù)的地址(也就是線程的入口點(diǎn))在threadFunc中指定。每個(gè)線程函數(shù)都必須具有這樣的原型:

            DWORD WINAPI threadfunc(LPVOID param);

            需要傳遞給新線程的任何參數(shù)都在CreateThread()的param中指定。線程函數(shù)在它的參數(shù)中接收這個(gè)32位的值。這個(gè)參數(shù)可以用作任何目的。函數(shù)返回它的退出狀態(tài)。

            參數(shù)flags確定了線程的執(zhí)行狀態(tài)。如果它是0,線程會(huì)立即執(zhí)行。如果是CREATE_SUSPEND,線程則以掛起狀態(tài)創(chuàng)建并等待執(zhí)行。(可以通過(guò)調(diào)用ResumeThread()來(lái)開(kāi)始執(zhí)行,稍后討論)。

            與線程相關(guān)的標(biāo)識(shí)符以threadID所指向的長(zhǎng)整型返回。

            如果成功,函數(shù)則向線程返回一個(gè)句柄。如果失敗,則返回NULL。可以通過(guò)調(diào)用CloseHandle()來(lái)顯式銷毀這個(gè)線程。否則,會(huì)在父進(jìn)程結(jié)束時(shí)自動(dòng)銷毀它。

            如前所述,當(dāng)線程的入口函數(shù)返回時(shí)終止執(zhí)行線程。進(jìn)程也可以使用TerminateThread()或者ExitThread()來(lái)手動(dòng)終止線程,這兩個(gè)函數(shù)的原型如下:

            BOOL TerminateThread(HANDLE thread, DWORD status);

            VOID ExitThread(DWORD status);

            對(duì)于TerminateThread(),thread是將要終止的線程的句柄。ExitThread()只能用來(lái)終止調(diào)用了ExitThread()的線程。對(duì)于兩個(gè)函數(shù)而言,status是終止?fàn)顟B(tài)。TerminateThread()如果成功,則會(huì)返回非0值,否則返回0。

            調(diào)用ExitThread()在功能上等價(jià)于允許線程函數(shù)正常返回。這意味著堆棧會(huì)正確地重新設(shè)置。當(dāng)使用TerminateThread()結(jié)束線程時(shí),線程會(huì)立刻終止,而會(huì)執(zhí)行任何特定的清理任務(wù)。另外,TerminateThread()可能會(huì)停止正在執(zhí)行重要操作的線程。為此,當(dāng)入口函數(shù)返回時(shí),通常最好(也是最容易的)讓線程正常終止。

            3.4.2 Visual C++對(duì)CreateThread()和ExitThread()的替換

            盡管CreateThread()和ExitThread()是用來(lái)創(chuàng)建并終止線程的Windows API函數(shù),我們?cè)诒菊虏⒉粫?huì)使用它們。原因是在Visual C++中(其他的Windows兼容的編譯器也可能有這個(gè)問(wèn)題)使用這兩個(gè)函數(shù)時(shí),會(huì)導(dǎo)致內(nèi)存泄漏,丟失少量的內(nèi)存。對(duì)于Visual C++,如果多線程程序利用了C/C++標(biāo)準(zhǔn)庫(kù)函數(shù)并使用了CreateThread()和ExitThread(),就會(huì)丟失少量的內(nèi)存。(如果您的程序沒(méi)有使用C/C++的標(biāo)準(zhǔn)庫(kù),就不會(huì)發(fā)生這樣的內(nèi)存丟失)。為了避免這種情況,必須使用Visual C++運(yùn)行庫(kù)中定義的函數(shù)來(lái)開(kāi)始和終止線程,而不是使用由Win32 API指定的函數(shù)。這些函數(shù)類似于CreateThread()和ExitThread(),但是不會(huì)產(chǎn)生內(nèi)存泄漏。

            提示:

            如果使用非Visual C++的編譯器,如果需要的話,檢查它的文檔來(lái)確定是否需要忽略CreateThread()和ExitThread(),以及如何做到這一點(diǎn)。

            Visual C++用_beginthreadex()和_endthreadex()來(lái)取代CreateThread()和ExitThread()。這兩個(gè)函數(shù)都需要頭文件<process.h>。下面是_beginthreadex()函數(shù)的原型:

            uintptr_t _beginthreadex(void *secAttr, unsigned stackSize,

            unsigned (__stdcall *threadFunc)(void *),

            void *param, unsigned flags,

            unsigned *threadID);

            正如您看到的那樣,_beginthreadex()的參數(shù)類似于CreateThread()的參數(shù)。另外,這些參數(shù)與CreateThread()指定的參數(shù)有相同的含義。secAttr是一個(gè)用來(lái)描述線程安全性屬性的指針。然而,如果secAttr為NULL,則會(huì)使用默認(rèn)的安全描述符。新線程堆棧的大小由stackSize參數(shù)按字節(jié)數(shù)傳遞。如果這個(gè)值為0,那么這個(gè)線程堆棧的大小與進(jìn)程中創(chuàng)建它的主線程的大小相同。

            線程函數(shù)的地址(也就是線程的入口點(diǎn))在threadFunc中指定。對(duì)于_beginthreadex(),線程函數(shù)的原型如下:

            unsigned_stdcall threadfunc(void *param)

            這個(gè)原型在功能上等價(jià)于CreateThread()的線程函數(shù)的原型,但是它使用了不同的類型名稱。想要傳遞給新線程的任何參數(shù)都在param參數(shù)中指定。

            flags參數(shù)確定線程的執(zhí)行狀態(tài)。如果flags參數(shù)為0,線程會(huì)立即開(kāi)始執(zhí)行。如果flags參數(shù)為CREATE_SUSPEND,則以掛起狀態(tài)創(chuàng)建線程。(可以調(diào)用ResumeThread()來(lái)開(kāi)始它)。與線程相關(guān)的標(biāo)識(shí)符以threadID指向的double word返回。

            如果成功,則這個(gè)函數(shù)返回一個(gè)線程的句柄;如果失敗,則返回0。類型uintptr_t指定了可以擁有指針或者句柄的Visual C++類型。

            _endthreadex()的原型如下:

            void _endthreadex(unsigned status);

            它的功能就像ExitThread()那樣,停止線程并返回status中指定的退出代碼(exit code)。

            由于Windows下使用最廣泛的編譯器是Visual C++,因此本章示例將使用_beginthreadex()和_endthreadex()而不是使用它們的等價(jià)的API函數(shù)。如果您使用了非Visual C++的編譯器,那么只需要用CreateThread()和EndThread()來(lái)替代這兩個(gè)函數(shù)。

            當(dāng)使用_beginthreadex()和_endthreadex()時(shí),必須記住鏈接多線程庫(kù)。這隨編譯器的不同而不同。在此有一些示例。當(dāng)使用Visual C++的命令行編譯器時(shí),包括-MT選項(xiàng)。為了在Visual C++ 6 IDE中使用多線程庫(kù),首先要激活“Project | Settings”屬性頁(yè)。然后選擇“C/C++”選項(xiàng)卡。接著在“Category”下拉列表框中選擇“Code Generation”,然后在“Use Runtime Library ”下拉列表框中選擇“Multithreaded”。對(duì)于Visual C++ 7 .NET IDE,選擇“Project |Properties”。然后選擇“C/C++”條目,并加亮顯示“Code Generation”。最后,將“Multithreaded”選擇為運(yùn)行庫(kù)。

            3.4.3 線程的掛起和恢復(fù)

            線程的執(zhí)行可以通過(guò)調(diào)用SuspendThread()來(lái)掛起。可以通過(guò)調(diào)用ResumeThread()來(lái)恢復(fù)它。這兩個(gè)函數(shù)的原型如下:

            DWORD SuspendThread(HANDLE hThread);

            DWORD ResumeThread(HANDLE hThread);

            兩個(gè)函數(shù)都通過(guò)hThread來(lái)傳遞線程的句柄。

            每個(gè)執(zhí)行的線程都有與其相關(guān)的掛起計(jì)數(shù)。如果這個(gè)計(jì)數(shù)為0,那么不會(huì)掛起線程。如果為非0值,則線程就會(huì)處于掛起狀態(tài)。每次調(diào)用SuspendThread()都會(huì)增加這個(gè)計(jì)數(shù)。每次調(diào)用ResumeThread()都會(huì)減小這個(gè)掛起計(jì)數(shù)。掛起的線程只有在它的掛起計(jì)數(shù)達(dá)到0時(shí)才會(huì)恢復(fù)。因此,為了恢復(fù)一個(gè)掛起的線程,對(duì)ResumeThread()的調(diào)用次數(shù)必須與對(duì)SuspendThread()的調(diào)用次數(shù)相等。

            這兩個(gè)函數(shù)都返回線程先前的掛起計(jì)數(shù),如果發(fā)生錯(cuò)誤,返回值為–1。

            3.4.4 改變線程的優(yōu)先級(jí)

            在Windows中,每個(gè)線程都與一個(gè)優(yōu)先級(jí)設(shè)置相關(guān)。線程的優(yōu)先級(jí)決定了線程接收的CPU時(shí)間的多少。低優(yōu)先級(jí)的線程接收比較少的時(shí)間,高優(yōu)先級(jí)的線程接收比較多的時(shí)間。當(dāng)然,線程接收的CPU時(shí)間的多少對(duì)于它的執(zhí)行性能以及它與系統(tǒng)中當(dāng)前執(zhí)行的其他線程之間的交互有著深遠(yuǎn)的影響。

            在Windows中,線程優(yōu)先級(jí)的設(shè)置是兩個(gè)值的組合:進(jìn)程總體的優(yōu)先級(jí)類別以及相對(duì)于這個(gè)優(yōu)先級(jí)類別的各個(gè)線程的優(yōu)先級(jí)設(shè)置。也就是說(shuō),線程實(shí)際的優(yōu)先級(jí)由進(jìn)程的優(yōu)先級(jí)類別和各個(gè)線程的優(yōu)先級(jí)的組合來(lái)確定。后面會(huì)逐一講述。

            1. 優(yōu)先級(jí)類別

            在默認(rèn)情況下,進(jìn)程具有普通的優(yōu)先級(jí)類別,大多數(shù)程序在其執(zhí)行的聲明周期內(nèi)保持這個(gè)普通的優(yōu)先級(jí)類別。盡管在本章的示例中沒(méi)有改變優(yōu)先級(jí)類別,但是為了完整起見(jiàn),在此給出了線程優(yōu)先級(jí)類別的簡(jiǎn)單概況。

            Windows定義了6個(gè)優(yōu)先級(jí)類別,相應(yīng)的值以從高到低的順序顯示如下:

            REALTIME_PRIORITY_CLASS

            HIGH_PRIORITY_CLASS

            ABOVE_NORMAL_PRIORITY_CLASS

            NORMAL_PRIORITY_CLASS

            BELOW_NORMAL_PRIORITY_CLASS

            IDLE_PRIORITY_CLASS

            在默認(rèn)情況下,程序的優(yōu)先級(jí)類別為NORMAL_PRIORITY_CLASS。通常,您不需要改變程序的優(yōu)先級(jí)類別。事實(shí)上,改變進(jìn)程的優(yōu)先級(jí)類別對(duì)于整個(gè)計(jì)算機(jī)系統(tǒng)的性能會(huì)有負(fù)面的影響。例如,如果您將一個(gè)程序的優(yōu)先級(jí)類別增加到REALTIME_PRIORITY_CLASS,它就會(huì)支配CPU。對(duì)于某些特殊的應(yīng)用程序,可能需要增加應(yīng)用程序的優(yōu)先級(jí)類別,但通常并不需要。如前所述,本章的應(yīng)用程序沒(méi)有改變優(yōu)先級(jí)類別。

            當(dāng)確實(shí)需要改變程序的優(yōu)先級(jí)類別時(shí),可以調(diào)用SetPriorityClass()。可以調(diào)用GetPriorityClass()來(lái)獲取當(dāng)前的優(yōu)先級(jí)類別。這兩個(gè)函數(shù)的原型如下:

            DWORD GetPriorityClass(HANDLE hApp);

            BOOL SetPriorityClass(HANDLE hApp, DWORD priority);

            在此,hApp是進(jìn)程的句柄。GetPriorityClass()返回應(yīng)用程序的優(yōu)先級(jí)類別,如果失敗的話,返回0。對(duì)于SetPriorityClass(),priority指定了進(jìn)程的新優(yōu)先級(jí)類別。

            2. 線程優(yōu)先級(jí)

            對(duì)于給定的優(yōu)先級(jí)類別,各個(gè)線程的優(yōu)先級(jí)確定了它在進(jìn)程內(nèi)接收的CPU時(shí)間的多少。當(dāng)線程第一次創(chuàng)建時(shí),它具有普通的優(yōu)先級(jí),但是您可以改變線程的優(yōu)先級(jí)—— 即使在它執(zhí) 行時(shí)。

            可以通過(guò)調(diào)用GetThreadPriority()來(lái)獲取線程的優(yōu)先級(jí)設(shè)置。可以使用SetThreadPriority()來(lái)增加或者減小線程的優(yōu)先級(jí)。這兩個(gè)函數(shù)的原型如下:

            BOOL SetThreadPriority(HANDLE hThread, int priority);

            int GetThreadPriority(HANDLE hThread);

            對(duì)于這兩個(gè)函數(shù)而言,hThread是線程的句柄。對(duì)于SetThreadPriority(),priority是新的優(yōu)先級(jí)設(shè)置。如果發(fā)生錯(cuò)誤,則返回值為0;否則,返回非0值。GetThreadPriority()會(huì)返回當(dāng)前的優(yōu)先級(jí)設(shè)置。優(yōu)先級(jí)設(shè)置按照從高到低的順序如表3-1所示。

            表3-1 優(yōu)先級(jí)設(shè)置

            線程優(yōu)先級(jí)

            THREAD_PRIORITY_TIME_CRITICAL

            15

            THREAD_PRIORITY_HIGHEST

            2

            THREAD_PRIORITY_ABOVE_NORMAL

            1

            THREAD_PRIORITY_NORMAL

            0

            THREAD_PRIORITY_BELOW_NORMAL

            -1

            THREAD_PRIORITY_LOWEST

            -2

            THREAD_PRIORITY_IDLE

            -15

            這些值相對(duì)于進(jìn)程的優(yōu)先級(jí)類別或增或減。通過(guò)組合進(jìn)程的優(yōu)先級(jí)類別和線程的優(yōu)先級(jí),Windows向應(yīng)用程序提供了31個(gè)不同的優(yōu)先級(jí)設(shè)置的支持。

            如果有錯(cuò)誤發(fā)生,則GetThreadPriority()返回THREAD_PRIORITY_ERROR_RETURN。

            在大多數(shù)情況下,如果線程具有普通的優(yōu)先級(jí)類別,那么可以隨意地改變它的優(yōu)先級(jí)設(shè)置,而不必?fù)?dān)心會(huì)給整個(gè)系統(tǒng)的性能帶來(lái)災(zāi)難性的影響。您將會(huì)看到,在下面部分開(kāi)發(fā)的線程控制面板中,可以改變進(jìn)程內(nèi)線程的優(yōu)先級(jí)設(shè)置(但是不能改變優(yōu)先級(jí)類別)。

            3.4.5 獲取主線程的句柄

            主線程的執(zhí)行是可以控制的。為此,需要獲取它的句柄。做到這一點(diǎn)最簡(jiǎn)單的方法是調(diào)用GetCurrentThread(),其原型如下:

            HANDLE GetCurrentThread(void);

            這個(gè)函數(shù)返回當(dāng)前線程的偽句柄(pseudohandle)。之所以稱之為偽句柄,是因?yàn)樗且粋€(gè)預(yù)定義的值,總是引用當(dāng)前的線程,而不是引用指定的調(diào)用線程。然而,它能夠用在任何可以使用普通線程處理的地方。

            3.4.6 同步

            在使用多線程或多進(jìn)程時(shí),有時(shí)需要調(diào)整兩個(gè)或者多個(gè)線程(或者進(jìn)程)之間的活動(dòng)。這個(gè)過(guò)程稱為同步。當(dāng)兩個(gè)或者多個(gè)線程需要訪問(wèn)共享資源,而這個(gè)共享資源在同一時(shí)刻只能由一個(gè)線程使用時(shí),就需要使用同步。例如,當(dāng)一個(gè)線程在寫(xiě)文件時(shí),在此時(shí)必須阻止另一個(gè)線程也這么做。同步的另一個(gè)原因是有時(shí)線程需要等待由另一個(gè)線程引發(fā)的事件。在此情況下,必須采取某種措施將第一個(gè)線程保持掛起狀態(tài),直到這個(gè)事件發(fā)生。隨后等待的線程必須恢復(fù) 執(zhí)行。

            通常某個(gè)任務(wù)會(huì)處于兩種狀態(tài)。首先,它可能正在執(zhí)行(或者在獲得它的時(shí)間段時(shí)就開(kāi)始執(zhí)行)。另外,任務(wù)可能被阻塞,等待某個(gè)資源或者事件。在此情況下其執(zhí)行被掛起,直到所需的資源可以使用或者所等待的事件發(fā)生。

            如果您對(duì)于同步問(wèn)題或者它的常用解決方案(信號(hào)量)不熟悉,下面的部分將對(duì)此進(jìn)行討論。

            1. 理解同步問(wèn)題

            Windows必須提供某種特殊的服務(wù)來(lái)允許對(duì)共享資源的訪問(wèn)同步,因?yàn)槿绻麤](méi)有操作系統(tǒng)的協(xié)助,進(jìn)程或者線程就沒(méi)有辦法得知它是否在單獨(dú)訪問(wèn)某個(gè)資源。為了理解這個(gè)問(wèn)題,假定您在為一個(gè)沒(méi)有提供任何同步支持的多任務(wù)操作系統(tǒng)編寫(xiě)程序,并且假定您具有兩個(gè)并發(fā)執(zhí)行的線程A和B,它們都不時(shí)地訪問(wèn)某個(gè)資源R(如磁盤(pán)文件),這個(gè)資源在某個(gè)時(shí)刻只能被一個(gè)線程訪問(wèn)。為了在一個(gè)線程使用這個(gè)資源時(shí)阻止另一個(gè)線程訪問(wèn)它,您嘗試了下面的解決方案。首先,創(chuàng)建了一個(gè)初始化值為0并且兩個(gè)線程都可以訪問(wèn)的變量,名為flag。然后,在使用訪問(wèn)R的每段代碼之前,等待flag被清0,然后設(shè)置flag,訪問(wèn)R,最后將flag清0。也就是說(shuō),在每個(gè)線程訪問(wèn)R之前,執(zhí)行如下的代碼:

            while(flag) ; // wait for flag to be cleared

            flag = 1; // set flag

            // ... access resource R ...

            flag = 0; // clear the flag

            這段代碼隱含的概念是,如果設(shè)置了flag,則兩個(gè)線程都不能夠訪問(wèn)R。從概念上講,這種方法是正確的解決方案。然而,實(shí)際上它遠(yuǎn)遠(yuǎn)沒(méi)有達(dá)到要求,原因很簡(jiǎn)單:它并非總是有效!讓我們看一下原因。

            使用剛才給定的代碼,有可能兩個(gè)進(jìn)程同時(shí)訪問(wèn)R。while循環(huán)在本質(zhì)上執(zhí)行重復(fù)的加載和比較flag上的指令。換句話說(shuō),它一直在測(cè)試flag的值。當(dāng)flag被清0的時(shí)候,代碼的下一行將設(shè)置flag的值。問(wèn)題在于,這兩個(gè)操作有可能在兩個(gè)不同的時(shí)間段執(zhí)行。在兩個(gè)時(shí)間段之間,flag的值有可能被另一個(gè)線程訪問(wèn),從而R被兩個(gè)線程同時(shí)訪問(wèn)。為了理解這一點(diǎn),假定線程A進(jìn)入while循環(huán),發(fā)現(xiàn)flag為0,這是訪問(wèn)R的綠燈。然而,在將flag設(shè)置為1之前,其時(shí)間段用盡,線程B恢復(fù)執(zhí)行。如果B執(zhí)行了它的while,它也發(fā)現(xiàn)flag沒(méi)有被設(shè)置,并且認(rèn)為它可以安全地訪問(wèn)R。然而,當(dāng)A重新開(kāi)始時(shí),它也會(huì)訪問(wèn)R。問(wèn)題的關(guān)鍵在于對(duì)flag的測(cè)試和設(shè)置沒(méi)有包含在一個(gè)連續(xù)的操作中,而是可以被分為兩個(gè)部分,正如剛才說(shuō)明的那樣。無(wú)論您如何努力,都沒(méi)有辦法只使用應(yīng)用層的代碼以絕對(duì)保證在同一時(shí)刻只有一個(gè)線程訪問(wèn)R。

            對(duì)同步問(wèn)題的解決方案簡(jiǎn)單而優(yōu)雅。操作系統(tǒng)(在Windows中)提供了一個(gè)例程,在一個(gè)連續(xù)的操作中完成對(duì)flag的測(cè)試和設(shè)置(如果可能的話)。用操作系統(tǒng)工程師的話來(lái)說(shuō),這就是所謂的測(cè)試和置位(test and set)操作。由于歷史的原因,用來(lái)控制對(duì)共享資源的訪問(wèn)并提供線程(以及進(jìn)程)間同步的標(biāo)記被稱為信號(hào)量。信號(hào)量是Windows同步系統(tǒng)的核心。

            2. Windows的同步對(duì)象

            Windows支持幾種類型的同步對(duì)象。第一種類型是經(jīng)典的信號(hào)量。當(dāng)使用信號(hào)量時(shí),可以完全同步資源,在此情況下只有一個(gè)進(jìn)程或者線程在同一時(shí)刻可以訪問(wèn)這個(gè)資源,或者信號(hào)量允許不超過(guò)一定數(shù)量的進(jìn)程或者線程在同一時(shí)刻訪問(wèn)資源。信號(hào)量使用計(jì)數(shù)器來(lái)實(shí)現(xiàn),當(dāng)某個(gè)任務(wù)使用信號(hào)量時(shí),計(jì)數(shù)器減小;當(dāng)這個(gè)任務(wù)釋放信號(hào)量時(shí),計(jì)數(shù)器增加。

            第二個(gè)同步對(duì)象是互斥體信號(hào)量,或者簡(jiǎn)稱為互斥體。互斥體將一個(gè)資源同步,保證在任何時(shí)候都只有一個(gè)線程或者進(jìn)程來(lái)訪問(wèn)它。在本質(zhì)上,互斥體是標(biāo)準(zhǔn)信號(hào)量的一個(gè)特殊版本。

            第三個(gè)同步對(duì)象是事件對(duì)象。它可以用來(lái)阻塞對(duì)某個(gè)資源的訪問(wèn),直到某個(gè)其他的進(jìn)程或者線程發(fā)送信號(hào),通知可以使用資源(也就是一個(gè)事件對(duì)象發(fā)送某個(gè)指定的事件發(fā)生的信號(hào))。

            第四個(gè)同步對(duì)象是可等待計(jì)時(shí)器。可等待計(jì)時(shí)器阻塞線程的執(zhí)行,直到指定的時(shí)間。也可以創(chuàng)建計(jì)時(shí)器序列,這是一個(gè)計(jì)時(shí)器的列表。

            可以使用臨界區(qū)對(duì)象將一個(gè)代碼段放入臨界區(qū),從而阻止在同一時(shí)刻一個(gè)以上的線程使用這段代碼。當(dāng)一個(gè)線程進(jìn)入臨界區(qū)時(shí),其他線程只有在第一個(gè)線程離開(kāi)整個(gè)臨界區(qū)時(shí)才可以使用它。

            本章使用的惟一的同步對(duì)象是互斥體,下面的部分將對(duì)其進(jìn)行描述。然而,C++程序員可以使用所有的Windows定義的同步對(duì)象。如前所述,這是使得C++依賴于操作系統(tǒng)處理多線程的主要優(yōu)點(diǎn)之一:所有的多線程特性都在您的控制之中。

            3. 使用互斥體同步線程

            如前所述,互斥體是一種特殊的信號(hào)量,在給定的時(shí)間內(nèi),只允許一個(gè)線程訪問(wèn)某個(gè)資源。在使用互斥體之前,必須使用CreatMutex()創(chuàng)建一個(gè)互斥體,函數(shù)原型如下:

            HANDLE CreateMutex(LPSECURITY_ATTRIBUTES secAttr,

            BOOL acquire,

            LPCSTR name);

            在此,secAttr是用來(lái)描述安全屬性的指針。如果secAttr為NULL,則使用默認(rèn)的安全描 述符。

            如果創(chuàng)建的線程需要互斥體的控制,則acquire為true,否則為false。

            name參數(shù)指向一個(gè)字符串,這個(gè)字符串是互斥體對(duì)象的名稱。互斥體是一個(gè)全局對(duì)象,它可能被其他進(jìn)程使用。為此,當(dāng)兩個(gè)進(jìn)程都打開(kāi)了使用相同名稱的互斥體時(shí),二者引用了相同的互斥體。使用這種方法可以將兩個(gè)進(jìn)程同步。這個(gè)名稱也可以為NULL,在此情況下這個(gè)信號(hào)量被限制在一個(gè)進(jìn)程之內(nèi)。

            如果成功,則CreatMutex()函數(shù)返回信號(hào)量的句柄,否則,返回NULL。當(dāng)主進(jìn)程結(jié)束時(shí),互斥體的句柄則自動(dòng)關(guān)閉。當(dāng)不再需要時(shí),可以調(diào)用CloseHandle()來(lái)顯式地關(guān)閉互斥體的句柄。

            當(dāng)創(chuàng)建信號(hào)量時(shí),可以調(diào)用兩個(gè)相關(guān)的函數(shù)來(lái)使用它:WaitForSingleObject()和ReleaseMutex()。這兩個(gè)函數(shù)的原型如下:

            DWORD WaitForSingleObject(HANDLE hObject, DWORD howLong);

            BOOL ReleaseMutex(HANDLE hMutex);

            WaitForSingleObject()等待一個(gè)同步對(duì)象,直到這個(gè)對(duì)象可以使用或者超時(shí)之后才會(huì)返回。在使用互斥體時(shí),hObject是互斥體的句柄。howLong參數(shù)以毫秒為單位指定調(diào)用例程的等待時(shí)間。當(dāng)這個(gè)時(shí)間用盡時(shí),會(huì)返回超時(shí)錯(cuò)誤。為了無(wú)限期地等待,可以使用值INFINITE。當(dāng)成功時(shí)(也就是訪問(wèn)被準(zhǔn)許),這個(gè)函數(shù)返回WAIT_OBJECT_0。當(dāng)發(fā)生超時(shí)時(shí),返回WAIT_TIMEOUT。

            ReleaseMutex()釋放互斥體,并允許其他線程獲取它。在此,hMutex是互斥體的句柄。如果成功,則函數(shù)返回非0值;如果失敗,則返回0。

            為了使用互斥體控制對(duì)共享資源的訪問(wèn),封裝了訪問(wèn)在調(diào)用WaitForSingleObject()和ReleaseMutex()之間的資源的代碼,如下面的代碼所示(當(dāng)然,超時(shí)期限隨應(yīng)用程序的不同而 不同)。

            if(WaitForSingleObject(hMutex, 10000)==WAIT_TIMEOUT) {

            // handle time-out error

            }

            // access the resource

            ReleaseMutex(hMutex);

            通常,您會(huì)選擇足夠長(zhǎng)的超時(shí)期限來(lái)適應(yīng)程序的操作。如果在開(kāi)發(fā)多線程應(yīng)用程序時(shí)重復(fù)出現(xiàn)超時(shí)錯(cuò)誤,那么通常意味著您創(chuàng)建了死鎖條件。當(dāng)一個(gè)線程等待另一個(gè)線程永遠(yuǎn)都不會(huì)釋放的互斥體時(shí),就會(huì)發(fā)生死鎖。

            posted on 2006-11-10 10:06 大龍 閱讀(7392) 評(píng)論(5)  編輯 收藏 引用

            評(píng)論

            # re: Windows線程函數(shù)概述 - 《C++編程藝術(shù)》[未登錄](méi) 2007-12-14 12:21 Corner Zhang

            好文,對(duì)我很有幫助,學(xué)習(xí)了!  回復(fù)  更多評(píng)論   

            # re: Windows線程函數(shù)概述 - 《C++編程藝術(shù)》 2010-03-12 10:29 invenco

            很不錯(cuò)  回復(fù)  更多評(píng)論   

            # re: Windows線程函數(shù)概述 - 《C++編程藝術(shù)》 2010-09-28 12:56 Vendy

            寫(xiě)得很不錯(cuò),很受益,頂!@invenco
              回復(fù)  更多評(píng)論   

            # re: Windows線程函數(shù)概述 - 《C++編程藝術(shù)》 2012-04-12 09:22 misserwell

            請(qǐng)問(wèn)線程,進(jìn)程的創(chuàng)建,是不是再不同的OS 里有不同的方法》 ?
            我很好奇, 在 linux 里 好像用 pthread_create 去創(chuàng)建線程, 據(jù)你所說(shuō), windows里 用CreateThread , 我說(shuō)的對(duì)嗎? 希望你再多講講  回復(fù)  更多評(píng)論   

            # re: Windows線程函數(shù)概述 - 《C++編程藝術(shù)》 2012-06-09 19:48 LXJ

            請(qǐng)問(wèn)一下,什么是線程函數(shù)?  回復(fù)  更多評(píng)論   


            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            99久久99久久精品国产| 久久91这里精品国产2020| 99久久精品免费看国产一区二区三区 | 97精品久久天干天天天按摩| 久久777国产线看观看精品| 99热精品久久只有精品| 午夜精品久久久久久毛片| 品成人欧美大片久久国产欧美...| 欧美伊人久久大香线蕉综合 | 99久久精品午夜一区二区| 久久久99精品一区二区| 99久久精品日本一区二区免费| 日本久久久久久久久久| 久久99精品国产麻豆宅宅| 香蕉久久夜色精品升级完成| 性高朝久久久久久久久久| 久久久久久狠狠丁香| 亚洲精品乱码久久久久久蜜桃不卡 | 久久久久av无码免费网| 久久av免费天堂小草播放| 99久久人妻无码精品系列| 色综合久久久久久久久五月| 亚洲伊人久久综合影院| 久久精品国产精品亜洲毛片| 久久精品国产亚洲沈樵| 久久99久久99精品免视看动漫| 亚洲精品无码久久久影院相关影片| 一本色道久久88综合日韩精品 | 欧美精品久久久久久久自慰| 免费精品久久天干天干| 久久亚洲色一区二区三区| 久久精品中文字幕一区| 精品国产热久久久福利| 国产免费久久精品99久久| 色噜噜狠狠先锋影音久久| 久久精品成人免费网站| 亚洲AV无码一区东京热久久| 久久丫精品国产亚洲av不卡 | 久久精品无码专区免费| 国产无套内射久久久国产| 91久久精品电影|