海水正藍

          面朝大海,春暖花開
          posts - 145, comments - 29, trackbacks - 0, articles - 1
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          本系列意在記錄Windwos線程的相關知識點,包括線程基礎、線程調度、線程同步、TLS、線程池等

           

          信號量內核對象

          信號量內核對象用來進行資源計數,它包含一個使用計數、最大資源數、當前資源計數。最大資源數表示信號量可以控制的最大資源數量,當前資源數表示信號當前可用的資源數量。

          設想一個場景:需要開發一個服務器進程,最多同時運行5個線程來響應客戶端請求,應該設計一個“線程池”。最開始的時候,5個線程都應該在等待狀 態,如果有一個客戶端請求到來,那么喚醒其中的一個線程以處理客戶端請求,如果同時的請求數量為5,那么5個線程將全部投入使用,再多的請求應該被放棄。 也就是說,隨著客戶端請求的增加,當前資源計數隨之遞減。

          我們可能需要這樣的一個內核對象來實現這個功能:初始化5個線程并同時等待一個內核對象觸發,當一個客戶端請求到來時,試圖觸發內核對象,這樣5個 線程中隨機一個被喚醒,并且自動使內核對象變為未觸發。外部判斷上限是否到達5。表面看來似乎用“自動重置的事件對象”即可實現這個功能啊,為什么要涉及 到信號量呢?因為信號量還可以控制一次喚醒多少個線程!!而且這個例子只是信號量的一個用途,后面我們會看到一個更實際的用途。

          總結一下,信號量內核對象是這樣的一種對象:它維護一個資源計數,當資源計數大于0,處于觸發狀態;資源計數等于0時,處于未觸發狀態;資源計數不可能小于0,也絕不可能大于資源計數上限。下圖展示了這種內核對象的特點:

          image

          如上圖,只有資源計數>0時才是觸發狀態,資源=0時為未觸發狀態,而WaitForSingleObject成功將遞減資源計數,調用ReleaseSemaphore將增加資源計數。

          下面兩個函數CreateSemaphoreCreateSemaphoreEx用于創建信號量對象:

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

          任何進程可以用OpenSemaphore來得到一個命名的信號量:

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

          線程通過調用ReleaseSemaphore來遞增資源計數,不一定每次只遞增1,可以設置遞增任意值。當將要超過資源上限值的時候,ReleaseSemaphore會返回FALSE。

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

           

          互斥量內核對象

          互斥量(mutex)用來確保一個線程獨占對一個資源的訪問。互斥量包含一個使用計數、線程ID和一個遞歸計數,互斥量與關鍵段的行為幾乎相同(因 為它記錄了線程ID和遞歸計數,使得互斥量可以支持遞歸調用的情況)。互斥量的規則十分簡單:如果線程ID為0(即沒有線程獨占它),那么它處于觸發狀 態,任何試圖等待該對象的線程都將獲得資源的獨占訪問;如果線程ID不為0,那么它處于未觸發狀態,任何試圖等待該對象的線程都將等待。

          可以使用CreateMutex或者CreateMutexEx創建互斥對象:

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

          一如既往,OpenMutex用于打開一個已經命名的互斥量內核對象:

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

          線程在獲得對獨占資源的訪問權限之后,可以正常執行相關的邏輯,當需要釋放互斥對象的時候可以調用ReleaseMutex

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

          互斥量與其他內核對象不同,它會記錄究竟是哪個線程占用了共享資源,結合遞歸計數,同一個線程可以在獲得共享資源之后繼續訪問共享資源,這個行為就像關鍵段一樣。然而互斥量和關鍵段從本質上是不同的,關鍵段是用戶模式的線程同步方法,而互斥量是內核模式的線程同步方式。

           

          介紹完這兩個內核對象后,我們思考一下前面在【Windows】線程漫談——線程同步之Slim讀/寫鎖中 設計的一個場景:有一個共享的隊列,2個服務端線程負責讀取隊列中的條目以處理,2個客戶端線程負責寫入隊列中的條目以使服務先端線程處理,當隊列中沒有 條目的時候應當掛起服務端線程,直到有條目進入時才被喚醒,另一方面,當隊列已滿時,客戶端線程應當掛起直到服務端至少處理了一個條目,以釋放至少一個條 目的空間。

          現在我們來用信號量和互斥量來實現同樣的功能,下面的流程圖分別是客戶端寫入線程和服務端讀取線程的邏輯:

          1.首先創建一個互斥量對象m_hmtxQ,并初始化為未觸發狀態;之后創建一個信號量對象,并設置最大資源計數為隊列的長度,初始化資源計數為0,正好表征隊列元素的個數。

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

          2.設計客戶端核心邏輯如下圖:

          image

          WatiForSingleObject:試圖獲得隊列的獨占訪問權限,對于這個隊列無論是讀還是寫都應該是線程獨占的。因此,使用互斥量對象來同步;

          ReleaseSemaphore:試圖增加一個資源計數,表征客戶端想要向隊列中增加一個元素,當然隊列可能現在已經滿了,對應的資源計數已達到 計數上限,此時ReleaseSemaphore會返回FALSE,這樣客戶端就不能像隊列中插入元素。反之,如果ReleaseSemaphore返回 TRUE,表示隊列沒有滿,客戶端可以向隊列中插入元素。

          ReleaseMutex:無論客戶端是否能夠像隊列中插入元素,在結束訪問后,都應該釋放互斥對象,以便其他線程能夠進入臨界資源。

          3.設計服務端核心邏輯如下圖:

          image

          WatiForSingleObject:試圖獲得隊列的獨占訪問權限,對于這個隊列無論是讀還是寫都應該是線程獨占的。因此,使用互斥量對象來同步;

          WaitForSingleObject(m_hsemNumElements…):試圖檢查信號量對象是否是觸發狀態。只有是觸發狀態的信號量對 象,線程才能進入;也就意味著:隊列中只要有元素(資源>0,觸發狀態),服務端就能讀取。反之,如果隊列中沒有元素(資源=0,未觸發狀態),服 務端將暫時不能訪問隊列,這時應該立即釋放Mutex。

          ReleaseMutex:無論客戶端是否能夠像隊列中插入元素,在結束訪問后,都應該釋放互斥對象,以便其他線程能夠進入臨界資源。



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

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 社会| 孙吴县| 甘德县| 福安市| 东乌珠穆沁旗| 武宣县| 东乌| 油尖旺区| 龙南县| 邵阳市| 炎陵县| 韶关市| 项城市| 卫辉市| 余江县| 沛县| 翁牛特旗| 昆山市| 柯坪县| 陆丰市| 将乐县| 手机| 西林县| 天门市| 迁西县| 宜宾市| 金坛市| 潞西市| 乌兰察布市| 鹤庆县| 上栗县| 丹凤县| 新巴尔虎右旗| 石景山区| 嘉禾县| 信阳市| 株洲县| 株洲市| 武陟县| 霞浦县| 沛县|