海水正藍(lán)

          面朝大海,春暖花開(kāi)
          posts - 145, comments - 29, trackbacks - 0, articles - 1
            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          本系列意在記錄Windwos線程的相關(guān)知識(shí)點(diǎn),包括線程基礎(chǔ)、線程調(diào)度、線程同步、TLS、線程池等

           

          信號(hào)量?jī)?nèi)核對(duì)象

          信號(hào)量?jī)?nèi)核對(duì)象用來(lái)進(jìn)行資源計(jì)數(shù),它包含一個(gè)使用計(jì)數(shù)、最大資源數(shù)、當(dāng)前資源計(jì)數(shù)。最大資源數(shù)表示信號(hào)量可以控制的最大資源數(shù)量,當(dāng)前資源數(shù)表示信號(hào)當(dāng)前可用的資源數(shù)量。

          設(shè)想一個(gè)場(chǎng)景:需要開(kāi)發(fā)一個(gè)服務(wù)器進(jìn)程,最多同時(shí)運(yùn)行5個(gè)線程來(lái)響應(yīng)客戶端請(qǐng)求,應(yīng)該設(shè)計(jì)一個(gè)“線程池”。最開(kāi)始的時(shí)候,5個(gè)線程都應(yīng)該在等待狀 態(tài),如果有一個(gè)客戶端請(qǐng)求到來(lái),那么喚醒其中的一個(gè)線程以處理客戶端請(qǐng)求,如果同時(shí)的請(qǐng)求數(shù)量為5,那么5個(gè)線程將全部投入使用,再多的請(qǐng)求應(yīng)該被放棄。 也就是說(shuō),隨著客戶端請(qǐng)求的增加,當(dāng)前資源計(jì)數(shù)隨之遞減。

          我們可能需要這樣的一個(gè)內(nèi)核對(duì)象來(lái)實(shí)現(xiàn)這個(gè)功能:初始化5個(gè)線程并同時(shí)等待一個(gè)內(nèi)核對(duì)象觸發(fā),當(dāng)一個(gè)客戶端請(qǐng)求到來(lái)時(shí),試圖觸發(fā)內(nèi)核對(duì)象,這樣5個(gè) 線程中隨機(jī)一個(gè)被喚醒,并且自動(dòng)使內(nèi)核對(duì)象變?yōu)槲从|發(fā)。外部判斷上限是否到達(dá)5。表面看來(lái)似乎用“自動(dòng)重置的事件對(duì)象”即可實(shí)現(xiàn)這個(gè)功能啊,為什么要涉及 到信號(hào)量呢?因?yàn)樾盘?hào)量還可以控制一次喚醒多少個(gè)線程?。《疫@個(gè)例子只是信號(hào)量的一個(gè)用途,后面我們會(huì)看到一個(gè)更實(shí)際的用途。

          總結(jié)一下,信號(hào)量?jī)?nèi)核對(duì)象是這樣的一種對(duì)象:它維護(hù)一個(gè)資源計(jì)數(shù),當(dāng)資源計(jì)數(shù)大于0,處于觸發(fā)狀態(tài);資源計(jì)數(shù)等于0時(shí),處于未觸發(fā)狀態(tài);資源計(jì)數(shù)不可能小于0,也絕不可能大于資源計(jì)數(shù)上限。下圖展示了這種內(nèi)核對(duì)象的特點(diǎn):

          image

          如上圖,只有資源計(jì)數(shù)>0時(shí)才是觸發(fā)狀態(tài),資源=0時(shí)為未觸發(fā)狀態(tài),而WaitForSingleObject成功將遞減資源計(jì)數(shù),調(diào)用ReleaseSemaphore將增加資源計(jì)數(shù)。

          下面兩個(gè)函數(shù)CreateSemaphoreCreateSemaphoreEx用于創(chuàng)建信號(hào)量對(duì)象:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          HANDLE WINAPI CreateSemaphore(
            __in_opt  LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,//內(nèi)核對(duì)象安全描述符
            __in      LONG lInitialCount,//資源計(jì)數(shù)的初始值
            __in      LONG lMaximumCount,//資源計(jì)數(shù)的最大值
            __in_opt  LPCTSTR lpName //內(nèi)核對(duì)象命名
          );
           
          HANDLE WINAPI CreateSemaphoreEx(
            __in_opt    LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
            __in        LONG lInitialCount,
            __in        LONG lMaximumCount,
            __in_opt    LPCTSTR lpName,
            __reserved  DWORD dwFlags,
            __in        DWORD dwDesiredAccess
          );

          任何進(jìn)程可以用OpenSemaphore來(lái)得到一個(gè)命名的信號(hào)量:

          1
          2
          3
          4
          5
          HANDLE WINAPI OpenSemaphore(
            __in  DWORD dwDesiredAccess,
            __in  BOOL bInheritHandle,
            __in  LPCTSTR lpName
          );

          線程通過(guò)調(diào)用ReleaseSemaphore來(lái)遞增資源計(jì)數(shù),不一定每次只遞增1,可以設(shè)置遞增任意值。當(dāng)將要超過(guò)資源上限值的時(shí)候,ReleaseSemaphore會(huì)返回FALSE。

          1
          2
          3
          4
          5
          BOOL WINAPI ReleaseSemaphore(
            __in       HANDLE hSemaphore,
            __in       LONG lReleaseCount,//可以設(shè)置遞增的值
            __out_opt  LPLONG lpPreviousCount//返回先前的資源計(jì)數(shù)
          );

           

          互斥量?jī)?nèi)核對(duì)象

          互斥量(mutex)用來(lái)確保一個(gè)線程獨(dú)占對(duì)一個(gè)資源的訪問(wèn)?;コ饬堪粋€(gè)使用計(jì)數(shù)、線程ID和一個(gè)遞歸計(jì)數(shù),互斥量與關(guān)鍵段的行為幾乎相同(因 為它記錄了線程ID和遞歸計(jì)數(shù),使得互斥量可以支持遞歸調(diào)用的情況)。互斥量的規(guī)則十分簡(jiǎn)單:如果線程ID為0(即沒(méi)有線程獨(dú)占它),那么它處于觸發(fā)狀 態(tài),任何試圖等待該對(duì)象的線程都將獲得資源的獨(dú)占訪問(wèn);如果線程ID不為0,那么它處于未觸發(fā)狀態(tài),任何試圖等待該對(duì)象的線程都將等待。

          可以使用CreateMutex或者CreateMutexEx創(chuàng)建互斥對(duì)象:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          HANDLE WINAPI CreateMutex(
            __in_opt  LPSECURITY_ATTRIBUTES lpMutexAttributes,
            __in      BOOL bInitialOwner,//初始化對(duì)象的狀態(tài),如果傳入FALSE則會(huì)初始化為觸發(fā)狀態(tài),如果傳入TRUE,那么對(duì)象的線程ID會(huì)被設(shè)置成當(dāng)前調(diào)用線程,并初始化為未觸發(fā)
            __in_opt  LPCTSTR lpName
          );
           
          HANDLE WINAPI CreateMutexEx(
            __in_opt  LPSECURITY_ATTRIBUTES lpMutexAttributes,
            __in_opt  LPCTSTR lpName,
            __in      DWORD dwFlags,
            __in      DWORD dwDesiredAccess
          );

          一如既往,OpenMutex用于打開(kāi)一個(gè)已經(jīng)命名的互斥量?jī)?nèi)核對(duì)象:

          1
          2
          3
          4
          5
          HANDLE WINAPI OpenMutex(
            __in  DWORD dwDesiredAccess,
            __in  BOOL bInheritHandle,
            __in  LPCTSTR lpName
          );

          線程在獲得對(duì)獨(dú)占資源的訪問(wèn)權(quán)限之后,可以正常執(zhí)行相關(guān)的邏輯,當(dāng)需要釋放互斥對(duì)象的時(shí)候可以調(diào)用ReleaseMutex

          1
          2
          3
          BOOL WINAPI ReleaseMutex(
            __in  HANDLE hMutex
          );

          互斥量與其他內(nèi)核對(duì)象不同,它會(huì)記錄究竟是哪個(gè)線程占用了共享資源,結(jié)合遞歸計(jì)數(shù),同一個(gè)線程可以在獲得共享資源之后繼續(xù)訪問(wèn)共享資源,這個(gè)行為就像關(guān)鍵段一樣。然而互斥量和關(guān)鍵段從本質(zhì)上是不同的,關(guān)鍵段是用戶模式的線程同步方法,而互斥量是內(nèi)核模式的線程同步方式。

           

          介紹完這兩個(gè)內(nèi)核對(duì)象后,我們思考一下前面在【W(wǎng)indows】線程漫談——線程同步之Slim讀/寫(xiě)鎖中 設(shè)計(jì)的一個(gè)場(chǎng)景:有一個(gè)共享的隊(duì)列,2個(gè)服務(wù)端線程負(fù)責(zé)讀取隊(duì)列中的條目以處理,2個(gè)客戶端線程負(fù)責(zé)寫(xiě)入隊(duì)列中的條目以使服務(wù)先端線程處理,當(dāng)隊(duì)列中沒(méi)有 條目的時(shí)候應(yīng)當(dāng)掛起服務(wù)端線程,直到有條目進(jìn)入時(shí)才被喚醒,另一方面,當(dāng)隊(duì)列已滿時(shí),客戶端線程應(yīng)當(dāng)掛起直到服務(wù)端至少處理了一個(gè)條目,以釋放至少一個(gè)條 目的空間。

          現(xiàn)在我們來(lái)用信號(hào)量和互斥量來(lái)實(shí)現(xiàn)同樣的功能,下面的流程圖分別是客戶端寫(xiě)入線程和服務(wù)端讀取線程的邏輯:

          1.首先創(chuàng)建一個(gè)互斥量對(duì)象m_hmtxQ,并初始化為未觸發(fā)狀態(tài);之后創(chuàng)建一個(gè)信號(hào)量對(duì)象,并設(shè)置最大資源計(jì)數(shù)為隊(duì)列的長(zhǎng)度,初始化資源計(jì)數(shù)為0,正好表征隊(duì)列元素的個(gè)數(shù)。

          1
          2
          m_hmtxQ = CreateMutex(NULL,FALSE,NULL);
          m_hsemNumElements = CreateSemaphore(NULL,0,nMaxElements,NULL);

          2.設(shè)計(jì)客戶端核心邏輯如下圖:

          image

          WatiForSingleObject:試圖獲得隊(duì)列的獨(dú)占訪問(wèn)權(quán)限,對(duì)于這個(gè)隊(duì)列無(wú)論是讀還是寫(xiě)都應(yīng)該是線程獨(dú)占的。因此,使用互斥量對(duì)象來(lái)同步;

          ReleaseSemaphore:試圖增加一個(gè)資源計(jì)數(shù),表征客戶端想要向隊(duì)列中增加一個(gè)元素,當(dāng)然隊(duì)列可能現(xiàn)在已經(jīng)滿了,對(duì)應(yīng)的資源計(jì)數(shù)已達(dá)到 計(jì)數(shù)上限,此時(shí)ReleaseSemaphore會(huì)返回FALSE,這樣客戶端就不能像隊(duì)列中插入元素。反之,如果ReleaseSemaphore返回 TRUE,表示隊(duì)列沒(méi)有滿,客戶端可以向隊(duì)列中插入元素。

          ReleaseMutex:無(wú)論客戶端是否能夠像隊(duì)列中插入元素,在結(jié)束訪問(wèn)后,都應(yīng)該釋放互斥對(duì)象,以便其他線程能夠進(jìn)入臨界資源。

          3.設(shè)計(jì)服務(wù)端核心邏輯如下圖:

          image

          WatiForSingleObject:試圖獲得隊(duì)列的獨(dú)占訪問(wèn)權(quán)限,對(duì)于這個(gè)隊(duì)列無(wú)論是讀還是寫(xiě)都應(yīng)該是線程獨(dú)占的。因此,使用互斥量對(duì)象來(lái)同步;

          WaitForSingleObject(m_hsemNumElements…):試圖檢查信號(hào)量對(duì)象是否是觸發(fā)狀態(tài)。只有是觸發(fā)狀態(tài)的信號(hào)量對(duì) 象,線程才能進(jìn)入;也就意味著:隊(duì)列中只要有元素(資源>0,觸發(fā)狀態(tài)),服務(wù)端就能讀取。反之,如果隊(duì)列中沒(méi)有元素(資源=0,未觸發(fā)狀態(tài)),服 務(wù)端將暫時(shí)不能訪問(wèn)隊(duì)列,這時(shí)應(yīng)該立即釋放Mutex。

          ReleaseMutex:無(wú)論客戶端是否能夠像隊(duì)列中插入元素,在結(jié)束訪問(wèn)后,都應(yīng)該釋放互斥對(duì)象,以便其他線程能夠進(jìn)入臨界資源。



          原文:http://www.cnblogs.com/P_Chou/archive/2012/07/13/semaphore-and-mutex-in-thread-sync.html

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 铜鼓县| 临澧县| 滁州市| 峨眉山市| 彭山县| 鄱阳县| 花垣县| 芮城县| 潍坊市| 赫章县| 富蕴县| 营口市| 山阴县| 河曲县| 东港市| 定安县| 黄冈市| 县级市| 重庆市| 广水市| 永泰县| 阿荣旗| 乐清市| 基隆市| 佛冈县| 枞阳县| 日喀则市| 兖州市| 晋城| 永寿县| 广灵县| 电白县| 长白| 永春县| 韶山市| 龙里县| 巴楚县| 扎兰屯市| 武汉市| 荥经县| 南充市|