1. 程式人生 > >CriticalSection、Event、Mutex、Semaphores區別

CriticalSection、Event、Mutex、Semaphores區別

臨界區(Critical Section)

    保證在某一時刻只有一個執行緒能訪問資料的簡便辦法。在任意時刻只允許一個執行緒對共享資源進行訪問。如果有多個執行緒試圖同時訪問臨界區,那麼在有一個執行緒進入後其他所有試圖訪問此臨界區的執行緒將被掛起,並一直持續到進入臨界區的執行緒離開。臨界區在被釋放後,其他執行緒可以繼續搶佔,並以此達到用原子方式操作共享資源的目的。

    臨界區包含兩個操作原語: EnterCriticalSection() 進入臨界區 LeaveCriticalSection() 離開臨界區

    EnterCriticalSection()語句執行後代碼將進入臨界區以後無論發生什麼,必須確保與之匹配的LeaveCriticalSection()都能夠被執行到。否則臨界區保護的共享資源將永遠不會被釋放。在使用臨界區時,一般不允許其執行時間過長,只要進入臨界區的執行緒還沒有離開,其他所有試圖進入此臨界區的執行緒都會被掛起而進入到等待狀態,並會在一定程度上影響。程式的執行效能。尤其需要注意的是不要將等待使用者輸入或是其他一些外界干預的操作包含到臨界區。如果進入了臨界區卻一直沒有釋放,同樣也會引起其他執行緒的長時間等待。換句話說,在執行了EnterCriticalSection()語句進入臨界區後無論發生什麼,必須確保與之匹配的LeaveCriticalSection()都能夠被執行到。可以通過新增結構化異常處理程式碼來確保LeaveCriticalSection()語句的執行。雖然臨界區同步速度很快,但卻只能用來同步本程序內的執行緒,而不可用來同步多個程序中的執行緒。

    MFC提供了很多功能完備的類,我用MFC實現了臨界區。MFC為臨界區提供有一個CCriticalSection類,使用該類進行執行緒同步處理是非常簡單的。只需線上程函式中用CCriticalSection類成員函式Lock()和UnLock()標定出被保護程式碼片段即可。Lock()後代碼用到的資源自動被視為臨界區內的資源被保護。UnLock後別的執行緒才能訪問這些資源。

互斥量(Mutex)

   互斥(Mutex)是一種用途非常廣泛的核心物件。能夠保證多個執行緒對同一共享資源的互斥訪問。同臨界區有些類似,只有擁有互斥物件的執行緒才具有訪問資源的許可權,由於互斥物件只有一個,因此就決定了任何情況下此共享資源都不會同時被多個執行緒所訪問。當前佔據資源的執行緒在任務處理完後應將擁有的互斥物件交出,以便其他執行緒在獲得後得以訪問資源。與其他幾種核心物件不同,互斥物件在作業系統中擁有特殊程式碼,並由作業系統來管理,作業系統甚至還允許其進行一些其他核心物件所不能進行的非常規操作。 互斥量跟臨界區很相似,只有擁有互斥物件的執行緒才具有訪問資源的許可權,由於互斥物件只有一個,因此就決定了任何情況下此共享資源都不會同時被多個執行緒所訪問。當前佔據資源的執行緒在任務處理完後應將擁有的互斥物件交出,以便其他執行緒在獲得後得以訪問資源。互斥量比臨界區複雜。因為使用互斥不僅僅能夠在同一應用程式不同執行緒中實現資源的安全共享,而且可以在不同應用程式的執行緒之間實現對資源的安全共享。

    以互斥核心物件來保持執行緒同步可能用到的函式主要有CreateMutex()、OpenMutex()、ReleaseMutex()、WaitForSingleObject()和WaitForMultipleObjects()等。在使用互斥物件前,首先要通過CreateMutex()或OpenMutex()建立或開啟一個互斥物件。CreateMutex()函式原型為:

HANDLE CreateMutex(
 LPSECURITY_ATTRIBUTES lpMutexAttributes, // 安全屬性指標
 BOOL bInitialOwner, // 初始擁有者
 LPCTSTR lpName // 互斥物件名
);

  引數bInitialOwner主要用來控制互斥物件的初始狀態。一般多將其設定為FALSE,以表明互斥物件在建立時並沒有為任何執行緒所佔有。如果在建立互斥物件時指定了物件名,那麼可以在本程序其他地方或是在其他程序通過OpenMutex()函式得到此互斥物件的控制代碼。OpenMutex()函式原型為:

HANDLE OpenMutex(
 DWORD dwDesiredAccess, // 訪問標誌
 BOOL bInheritHandle, // 繼承標誌
 LPCTSTR lpName // 互斥物件名
);

  當目前對資源具有訪問權的執行緒不再需要訪問此資源而要離開時,必須通過ReleaseMutex()函式來釋放其擁有的互斥物件,其函式原型為:

BOOL ReleaseMutex(HANDLE hMutex);

  其唯一的引數hMutex為待釋放的互斥物件控制代碼。至於WaitForSingleObject()和WaitForMultipleObjects()等待函式在互斥物件保持執行緒同步中所起的作用與在其他核心物件中的作用是基本一致的,也是等待互斥核心物件的通知。但是這裡需要特別指出的是:在互斥物件通知引起呼叫等待函式返回時,等待函式的返回值不再是通常的WAIT_OBJECT_0(對於WaitForSingleObject()函式)或是在WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1之間的一個值(對於WaitForMultipleObjects()函式),而是將返回一個WAIT_ABANDONED_0(對於WaitForSingleObject()函式)或是在WAIT_ABANDONED_0到WAIT_ABANDONED_0+nCount-1之間的一個值(對於WaitForMultipleObjects()函式)。以此來表明執行緒正在等待的互斥物件由另外一個執行緒所擁有,而此執行緒卻在使用完共享資源前就已經終止。除此之外,使用互斥物件的方法在等待執行緒的可排程性上同使用其他幾種核心物件的方法也有所不同,其他核心物件在沒有得到通知時,受呼叫等待函式的作用,執行緒將會掛起,同時失去可排程性,而使用互斥的方法卻可以在等待的同時仍具有可排程性,這也正是互斥物件所能完成的非常規操作之一。

  在編寫程式時,互斥物件多用在對那些為多個執行緒所訪問的記憶體塊的保護上,可以確保任何執行緒在處理此記憶體塊時都對其擁有可靠的獨佔訪問權。

    互斥物件在MFC中通過CMutex類進行表述。使用CMutex類的方法非常簡單,在構造CMutex類物件的同時可以指明待查詢的互斥物件的名字,在建構函式返回後即可訪問此互斥變數。CMutex類也是隻含有建構函式這唯一的成員函式,當完成對互斥物件保護資源的訪問後,可通過呼叫從父類CSyncObject繼承的UnLock()函式完成對互斥物件的釋放。CMutex類建構函式原型為:

CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );

  該類的適用範圍和實現原理與API方式建立的互斥核心物件是完全類似的,但要簡潔的多。

訊號量(Semaphores)

    訊號量物件對執行緒的同步方式與前面幾種方法不同,訊號允許多個執行緒同時使用共享資源,這與作業系統中的PV操作相同。它指出了同時訪問共享資源的執行緒最大數目。它允許多個執行緒在同一時刻訪問同一資源,但是需要限制在同一時刻訪問此資源的最大執行緒數目。在用CreateSemaphore()建立訊號量時即要同時指出允許的最大資源計數和當前可用資源計數。一般是將當前可用資源計數設定為最大資源計數,每增加一個執行緒對共享資源的訪問,當前可用資源計數就會減1,只要當前可用資源計數是大於0的,就可以發出訊號量訊號。但是當前可用計數減小到0時則說明當前佔用資源的執行緒數已經達到了所允許的最大數目,不能在允許其他執行緒的進入,此時的訊號量訊號將無法發出。執行緒在處理完共享資源後,應在離開的同時通過ReleaseSemaphore()函式將當前可用資源計數加1。在任何時候當前可用資源計數決不可能大於最大資源計數。 訊號量是通過計數來對執行緒訪問資源進行控制的,而實際上訊號量確實也被稱作Dijkstra計數器。


    PV操作及訊號量的概念都是由荷蘭科學家E.W.Dijkstra提出的。訊號量S是一個整數,S大於等於零時代表可供併發程序使用的資源實體數,但S小於零時則表示正在等待使用共享資源的程序數。

    P操作申請資源: 
    (1)S減1; 
    (2)若S減1後仍大於等於零,則程序繼續執行; 
    (3)若S減1後小於零,則該程序被阻塞後進入與該訊號相對應的佇列中,然後轉入程序排程。 
   
    V操作 釋放資源: 
    (1)S加1; 
    (2)若相加結果大於零,則程序繼續執行; 
    (3)若相加結果小於等於零,則從該訊號的等待佇列中喚醒一個等待程序,然後再返回原程序繼續執行或轉入程序排程。

    使用訊號量核心物件進行執行緒同步主要會用到CreateSemaphore()、OpenSemaphore()、ReleaseSemaphore()、WaitForSingleObject()和WaitForMultipleObjects()等函式。其中,CreateSemaphore()用來建立一個訊號量核心物件,其函式原型為:

HANDLE CreateSemaphore(
 LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // 安全屬性指標
 LONG lInitialCount, // 初始計數
 LONG lMaximumCount, // 最大計數
 LPCTSTR lpName // 物件名指標
);

  引數lMaximumCount是一個有符號32位值,定義了允許的最大資源計數,最大取值不能超過4294967295。lpName引數可以為建立的訊號量定義一個名字,由於其建立的是一個核心物件,因此在其他程序中可以通過該名字而得到此訊號量。OpenSemaphore()函式即可用來根據訊號量名開啟在其他程序中建立的訊號量,函式原型如下:

HANDLE OpenSemaphore(
 DWORD dwDesiredAccess, // 訪問標誌
 BOOL bInheritHandle, // 繼承標誌
 LPCTSTR lpName // 訊號量名
);

  線上程離開對共享資源的處理時,必須通過ReleaseSemaphore()來增加當前可用資源計數。否則將會出現當前正在處理共享資源的實際執行緒數並沒有達到要限制的數值,而其他執行緒卻因為當前可用資源計數為0而仍無法進入的情況。ReleaseSemaphore()的函式原型為:

BOOL ReleaseSemaphore(
 HANDLE hSemaphore, // 訊號量控制代碼
 LONG lReleaseCount, // 計數遞增數量
 LPLONG lpPreviousCount // 先前計數
);

  該函式將lReleaseCount中的值新增給訊號量的當前資源計數,一般將lReleaseCount設定為1,如果需要也可以設定其他的值。WaitForSingleObject()和WaitForMultipleObjects()主要用在試圖進入共享資源的執行緒函式入口處,主要用來判斷訊號量的當前可用資源計數是否允許本執行緒的進入。只有在當前可用資源計數值大於0時,被監視的訊號量核心物件才會得到通知。

  訊號量的使用特點使其更適用於對Socket(套接字)程式中執行緒的同步。例如,網路上的HTTP伺服器要對同一時間內訪問同一頁面的使用者數加以限制,這時可以為沒一個使用者對伺服器的頁面請求設定一個執行緒,而頁面則是待保護的共享資源,通過使用訊號量對執行緒的同步作用可以確保在任一時刻無論有多少使用者對某一頁面進行訪問,只有不大於設定的最大使用者數目的執行緒能夠進行訪問,而其他的訪問企圖則被掛起,只有在有使用者退出對此頁面的訪問後才有可能進入。

在MFC中,通過CSemaphore類對訊號量作了表述。該類只具有一個建構函式,可以構造一個訊號量物件,並對初始資源計數、最大資源計數、物件名和安全屬性等進行初始化,其原型如下:

CSemaphore( LONG lInitialCount = 1, LONG lMaxCount = 1, LPCTSTR pstrName = NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL );

  在構造了CSemaphore類物件後,任何一個訪問受保護共享資源的執行緒都必須通過CSemaphore從父類CSyncObject類繼承得到的Lock()和UnLock()成員函式來訪問或釋放CSemaphore物件。與前面介紹的幾種通過MFC類保持執行緒同步的方法類似,通過CSemaphore類也可以將前面的執行緒同步程式碼進行改寫,這兩種使用訊號量的執行緒同步方法無論是在實現原理上還是從實現結果上都是完全一致的。

事件(Event)

    事件物件也可以通過通知操作的方式來保持執行緒的同步。並且可以實現不同程序中的執行緒同步操作。

    訊號量包含的幾個操作原語: 
    CreateEvent() 建立一個訊號量 
    OpenEvent() 開啟一個事件 
    SetEvent() 回置事件 
    WaitForSingleObject() 等待一個事件 
    WaitForMultipleObjects() 等待多個事件

    使用臨界區只能同步同一程序中的執行緒,而使用事件核心物件則可以對程序外的執行緒進行同步,其前提是得到對此事件物件的訪問權。可以通過OpenEvent()函式獲取得到,其函式原型為:

HANDLE OpenEvent(
 DWORD dwDesiredAccess, // 訪問標誌
 BOOL bInheritHandle, // 繼承標誌
 LPCTSTR lpName // 指向事件物件名的指標
);

  如果事件物件已建立(在建立事件時需要指定事件名),函式將返回指定事件的控制代碼。對於那些在建立事件時沒有指定事件名的事件核心物件,可以通過使用核心物件的繼承性或是呼叫DuplicateHandle()函式來呼叫CreateEvent()以獲得對指定事件物件的訪問權。在獲取到訪問權後所進行的同步操作與在同一個程序中所進行的執行緒同步操作是一樣的。

  如果需要在一個執行緒中等待多個事件,則用WaitForMultipleObjects()來等待。WaitForMultipleObjects()與WaitForSingleObject()類似,同時監視位於控制代碼陣列中的所有控制代碼。這些被監視物件的控制代碼享有平等的優先權,任何一個控制代碼都不可能比其他控制代碼具有更高的優先權。WaitForMultipleObjects()的函式原型為:

DWORD WaitForMultipleObjects(
 DWORD nCount, // 等待控制代碼數
 CONST HANDLE *lpHandles, // 控制代碼陣列首地址
 BOOL fWaitAll, // 等待標誌
 DWORD dwMilliseconds // 等待時間間隔
);

  引數nCount指定了要等待的核心物件的數目,存放這些核心物件的陣列由lpHandles來指向。fWaitAll對指定的這nCount個核心物件的兩種等待方式進行了指定,為TRUE時當所有物件都被通知時函式才會返回,為FALSE則只要其中任何一個得到通知就可以返回。dwMilliseconds在這裡的作用與在WaitForSingleObject()中的作用是完全一致的。如果等待超時,函式將返回WAIT_TIMEOUT。如果返回WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1中的某個值,則說明所有指定物件的狀態均為已通知狀態(當fWaitAll為TRUE時)或是用以減去WAIT_OBJECT_0而得到發生通知的物件的索引(當fWaitAll為FALSE時)。如果返回值在WAIT_ABANDONED_0與WAIT_ABANDONED_0+nCount-1之間,則表示所有指定物件的狀態均為已通知,且其中至少有一個物件是被丟棄的互斥物件(當fWaitAll為TRUE時),或是用以減去WAIT_OBJECT_0表示一個等待正常結束的互斥物件的索引(當fWaitAll為FALSE時)。

MFC為事件相關處理也提供了一個CEvent類,共包含有除建構函式外的4個成員函式PulseEvent()、ResetEvent()、SetEvent()和UnLock()。在功能上分別相當與Win32 API的PulseEvent()、ResetEvent()、SetEvent()和CloseHandle()等函式。而建構函式則履行了原CreateEvent()函式建立事件物件的職責,其函式原型為:

CEvent(BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );

  按照此預設設定將建立一個自動復位、初始狀態為復位狀態的沒有名字的事件物件。封裝後的CEvent類使用起來更加方便,

事件可以實現不同程序中的執行緒同步操作,並且可以方便的實現多個執行緒的優先比較等待操作,例如寫多個WaitForSingleObject來代替WaitForMultipleObjects從而使程式設計更加靈活。

總結:

    1. 互斥量與臨界區的作用非常相似,但互斥量是可以命名的,也就是說它可以跨越程序使用。所以建立互斥量需要的資源更多,所以如果只為了在程序內部是用的話使用臨界區會帶來速度上的優勢並能夠減少資源佔用量。因為互斥量是跨程序的互斥量一旦被建立,就可以通過名字開啟它。

    2. 互斥量(Mutex),訊號燈(Semaphore),事件(Event)都可以被跨越程序使用來進行同步資料操作,而其他的物件與資料同步操作無關,但對於程序和執行緒來講,如果程序和執行緒在執行狀態則為無訊號狀態,在退出後為有訊號狀態。所以可以使用WaitForSingleObject來等待程序和執行緒退出。

    3. 通過互斥量可以指定資源被獨佔的方式使用,但如果有下面一種情況通過互斥量就無法處理,比如現在一位使用者購買了一份三個併發訪問許可的資料庫系統,可以根據使用者購買的訪問許可數量來決定有多少個執行緒/程序能同時進行資料庫操作,這時候如果利用互斥量就沒有辦法完成這個要求,訊號燈物件可以說是一種資源計數器。

相關推薦

Go語言學習之sync包(臨時物件池Pool互斥鎖Mutex等待Cond)(the way to go)

golang的特點就是語言層面支援併發,並且實現併發非常簡單,只需在需要併發的函式前面新增關鍵字go。 但是如何處理go併發機制中不同goroutine之間的同步與通訊,golang 中提供了sync包來解決相關的問題,當然還有其他的方式比如channel,原子操作atomic等等,這裡先

CriticalSectionEventMutexSemaphores區別

臨界區(Critical Section)     保證在某一時刻只有一個執行緒能訪問資料的簡便辦法。在任意時刻只允許一個執行緒對共享資源進行訪問。如果有多個執行緒試圖同時訪問臨界區,那麼在有一個執行緒進入後其他所有試圖訪問此臨界區的執行緒將被掛起,並一直持續到進入臨界

關於js中return falseevent.preventDefault()和event.stopPropagation()區別,以及阻止事件冒泡和阻止預設事件

在平時專案中,如果遇到需要阻止瀏覽器預設行為,大家經常會用return false;和event.preventDefault()來阻止,但對它倆的區別還是模糊,這裡順便帶上event.stopPropagation()一起區分下。 事件處理程式的返回值只對通過屬性註冊的處理程式才有意義,如果我

多執行緒之mutexsemaphore區別

多執行緒 在講多執行緒同步關鍵詞之前,先說一下單道程式。在單道程式中,每時每刻只有一個程式執行,該程式獨自佔據cpu。只有當該程式完成之後才釋放其佔據的所有資源。因為只有一個程式在執行,佔據cpu和訪問記憶體,沒有其他程式干擾。所以所有操作肯定是順序完成的,即滿足順序行、封

malloccallocrealloc和alloca各種的區別

calloc 一次 單元 不支持 new span 初始化 har 堆棧 需要先包含頭文件 #include"malloc.h" malloc是標準的在堆中開辟新的空間 比如 char *pt=(char *)malloc(10*sizeof(char)); 需要free(

call() apply() bind()方法的作用和區別

調用 權威指南 () 使用 func 開始 把他 對象 bsp 從一開始,我是在書上看到關於bind()、call() 和 apply(), 不過長久以來,在工作中與網上接觸到了很多關於這三個方法的使用場景,對這三個方法也算是比較熟悉了。所以把他們的作用和區別簡單闡述一下!

Shell重定向&>file2>&11>&2的區別

宋體 -s adding 必須 輸出 說話 如何 特殊 null 轉自:http://www.360doc.com/content/13/0523/16/7044580_287544243.shtml shell上:0表示標準輸入1表示標準輸出2表示標準錯誤輸出

go語言sync包的學習(MutexWaitGroupCond)

pri lee 拷貝 light 等待 runt broadcast 計算 混亂 package main; import ( "fmt" "sync" "runtime" "time" ) //加鎖,註意鎖要以指針的形式傳進來,不然只是拷

FtpFtps與Sftp之間的區別

進行 down 不同的 在那 用戶目錄 方案 重要 新的 信息安全 Ftp FTP 是File Transfer Protocol(文件傳輸協議)的英文簡稱,而中文簡稱為“文傳協議”。用於Internet上的控制文件的雙向傳輸。同時,它也是一個應用程序(Applicatio

SAP內存ABAP內存共享內存的 區別

用戶 cti 語句 strong 使用 append 共享 str 區別 區別: (1)SAP內存使用 SET/GET parameters 方法; SET PARAMETER ID ‘MAT’ field P_MATNR. GET PARAMETER ID ‘

轉載----executeexecuteQuery和executeUpdate之間的區別

als del mman 必須 ont 修改 效果 一次 都是 JDBCTM中Statement接口提供的execute、executeQuery和executeUpdate之間的區別 Statement 接口提供了三種執行 SQL 語句的方法:executeQuery、e

單元測試集成測試系統測試和驗收測試的聯系和區別

是否 功能 條件 黑盒測試 模塊 期望值 設計 tex 代碼 根據不同的測試階段,測試可以分為單元測試、集成測試、系統測試和驗收測試體現了測試由小到大、又內至外、循序漸進的測試過程和分而治之的思想。 單元測試的粒度最小,一般由開發小組采用白盒方式來測試,主要測試單元是

jQuery中$(function(){})與(function($){})(jQuery)$(document).ready(function(){})等的區別詳細講解

lib div 有效 cti title jquer init str lac 1、(function($) {…})(jQuery); 在(function($) {…})(jQuery)在內部定義的函數和變量只能在此範圍內有效。 形成是否

Echo()print()print_r()區別

表達式 類型變量 一個 pri truct ray echo 格式化 輸出 echo可以一次輸出多個值,多個值之間用逗號分隔。echo是語言結構(language construct),而並不是真正的函數,因此不能作為表達式的一部分使用。echo是php的內部指令,不是函

HTML行內元素塊狀元素行內塊狀元素的區別

height 方式 mar 兩個 code 代碼 ext light body   HTML可以將元素分類方式分為行內元素、塊狀元素和行內塊狀元素三種。首先需要說明的是,這三者是可以互相轉換的,使用display屬性能夠將三者任意轉換:   (1)display:inlin

StringStringBuffer與StringBuilder三者的區別

不可變 abcd ges 區別 ++ 源碼 重新 strong blog 簡單的說: String:創建的是字符串常量,創建的字符串會放入內存的常量池中,是不可變的對象。如果要對String類型的內容進行改變,實際上每次改變都會重新new一個String類型的字符串對象,指

shellcmddos和腳本語言區別和聯系

同時 environ 正常 erl urn exe comm displays 進行 問題一:DOS與windows中cmd區別 在windows系統中,“開始-運行-cmd”可以打開“cmd.exe”,進行命令行操作。 操作系統可以分成核心(kernel)和Shell

C#中數組ArrayList和List三者的區別

collect comm 兩個 根據 -s lis 數據打包 功能 target 在C#中數組,ArrayList,List都能夠存儲一組對象,那麽這三者到底有什麽樣的區別呢。 數組 數組在C#中最早出現的。在內存中是連續存儲的,所以它的索引速度非常快,而且賦值

IPTV互聯網電視網絡電視智能電視,這些概念有什麽區別

基本上 internet 未來 內容 設備 targe 即將 有線電視 home 網友問題:IPTV、互聯網電視、網絡電視、智能電視,這些概念有什麽區別? 網友答復: 1. IPTV、互聯網電視、網絡電視是不同形式的電視服務業務模式,智能電視是終端,兩個部分要分開回答。 2

jQuery中$(function(){})與(function($){})(jQuery)$(document).ready(function(){})等的區別詳細講解 ----轉載

最大 閉包 param 作用 alt dsm tracking 參數 bsp 1、(function($) {…})(jQuery); 1)、原理: 這實際上是匿名函數,如下: function(arg){…} 這就定義了一個匿名函數,參數為arg 而調用函數時,是在