小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

同步

 quasiceo 2013-09-11

同步---CriticalSection,Mutex,Event,Semaphores  

2011-09-22 13:49:46|  分類: Win32---API |  標簽:線程同步   |字號 訂閱

 關于線程的同步對象可分為內(nèi)核對象與非內(nèi)核對象,最大區(qū)別在于內(nèi)核對象能跨越進程,而非內(nèi)核對象不能跨越進程,只能同步單個進程中的線程。

 內(nèi)核對象:(非內(nèi)核對象: CriticalSection)

 1. 進程,Processe         2. 線程,Threads          3. 文件,F(xiàn)iles                  4. 控制臺輸入,Console input  

 5. 文件變化通知,F(xiàn)ile change notifications         6. 互斥量,Mutexes          7. 信號量,Semaphores

 8. 事件Events              9. 可等的計時器Waitable timers         10.Jobs

 每一個上面這些類型的對象都可以處于兩種狀態(tài)之一:有信號(signaled)和無信號(nonsignaled)??捎镁褪怯行盘枲顟B(tài),被占用就是無信號狀態(tài)。比如進程和線程在終結時其內(nèi)核對象變?yōu)橛行盘枺谒鼈兲幱趧?chuàng)建和正在運行時,其內(nèi)核對象是無信號的。

內(nèi)核對象同步應用:

1.某線程獲得某進程的內(nèi)核對象句柄時,可以改變進程優(yōu)先級、獲得進程的退出碼;使本線程與某進程的終結取得同步等。

2.當獲得某線程的內(nèi)核對象句柄時,可以改變該線程運行狀態(tài)、與該線程的終結取得同步等。

3.當獲得文件句柄時,本線程可與某一個異步文件的I/O操作獲得同步等。

4.控制臺輸入對象可用來使線程在有輸入進入時被喚醒以執(zhí)行相關任務等。

5.其它內(nèi)核對象―――文件改變通知、互斥量、信號量、事件、可等計時器等―――都只是為了同步對象而存在

 

下面詳細介紹一下常用的同步對象:

CritiaclSection:

        臨界區(qū)是保證在某一時刻只有一個線程能訪問數(shù)據(jù)的簡便辦法。在任意時刻只允許一個線程對共享資源進行訪問,如果有多個線程試圖同時訪問臨界區(qū),那么在有一個線程進入后其他所有試圖訪問此臨界區(qū)的線程將被掛起,并一直持續(xù)到進入臨界區(qū)的線程離開。臨界區(qū)在被釋放后,其他線程可以繼續(xù)搶占,并以此達到用原子方式操作共享資源的目的。

       在所有同步對象中,臨界區(qū)是最容易使用的,但它只能用于同步單個進程中的線程,并且不是內(nèi)核對象,它不由操作系統(tǒng)的低級部件管理,而且不能使用句柄來操縱,由于不是內(nèi)核對象,使得它作為一種輕量級的同步機制,同步速度比較快。

使用步驟:

        1.在進程中創(chuàng)建一個臨界區(qū),即在進程中分配一個CRITICAL_SECTION數(shù)據(jù)結構,該臨界區(qū)結構的分配必須是全局的,這樣該進程的不同線程就能訪問它。關于CRITICAL_SECTION結構體的深入分析,可以參見文章<<Break Free of Code Deadlocks in Critical Sections Under Windows>>

2.在使用臨界區(qū)同步線程之前,必須調用InitializeCriticalSection來初始化臨界區(qū)。在釋放資源之前,只需要初始化一次。

3.VOID EnterCriticalSection:阻塞函數(shù)。調用線程不能獲取指定臨界區(qū)的所有權時,該線程將睡眠,且在被喚醒之前,系統(tǒng)不會給它分配CPU?;蛘呤褂肨ryEnterCriticalSection方法嘗試進入臨界區(qū),如果進入成功,則調用者線程獲得臨界區(qū)的使用權,否則返回失敗。

4.執(zhí)行臨界區(qū)內(nèi)的任務。

5.BOOL LeaveCriticalSection:非阻塞函數(shù)。將當前線程對指定臨界區(qū)的引用計數(shù)減1;在使用計數(shù)變?yōu)榱銜r,另一等待此臨界區(qū)的一個線程將被喚醒。

6.當不需要再使用該臨界區(qū)時,使用DeleteCriticalSection來釋放臨界區(qū)需要的資源。此函數(shù)執(zhí)行后,再也不能使用EnterCriticalSection和LeaveCriticalSection,除非再次使用InitializeCriticalSection初始化了該臨界區(qū)。

注意事項:

1.臨界區(qū)一次只允許一個線程訪問,每個線程必須在試圖操作臨界區(qū)域數(shù)據(jù)之前調用該臨界區(qū)域標志(即一個CRITICAL_SECTION全局變量)EnterCriticalSection后,其它想要獲得訪問權的線程都會置于睡眠狀態(tài),且在被喚醒以前,系統(tǒng)將停止為它們分配CPU時間片。換言之,臨界區(qū)可以且僅可被一個線程擁有,當然,沒有任何線程調用EnterCriticalSection或TryEnterCriticalSection時,臨界區(qū)不屬于任何 一個線程。

2.當擁有臨界區(qū)所有權的線程調用LeaveCriticalSection放棄所有權時,系統(tǒng)只喚醒等待隊列中的一個線程,給它所有權,其它線程則繼續(xù)等待。

3.注意,擁有該臨界區(qū)的線程,每一次針對此臨界區(qū)的EnterCriticalSection調用都會成功(這里指的是重復調用也會立即返回,也就是支持嵌套調用),且會使得臨界區(qū)標志(即一個CRITICAL_SECTION全局變量)的引用計數(shù)增加1。在另一個線程能夠擁有該臨界區(qū)之前,擁有它的線程必須調用LeaveCriticalSection足夠多次,在引用計數(shù)降為零后,另一線程才有可能擁有該臨界區(qū)。換言之,在一個正常使用臨界區(qū)的線程中,calSection和LeaveCriticalSection應該成對使用。

4.TryEnterCriticalSection
BOOL TryEnterCriticalSection( LPCRITICAL_SECTION
lpCriticalSection );
從函數(shù)聲明便可看出,EnterCriticalSection函數(shù)的返回值為VOID,而這里為BOOL??梢妼τ赥ryEnterCriticalSection的調用,需要我們判斷其返回值。在調用TryEnterCriticalSection時,如果指定的臨界區(qū)沒有被任何線程(或還沒有被任何調用線程)擁有,該函數(shù)將臨界區(qū)的訪問權給予調用的線程,并返回TRUE;不過,如果臨界區(qū)已經(jīng)被另一個線程擁有,它立刻返回FALSE值。TryEnterCriticalSection和EnterCriticalSection之間的最大區(qū)別在于TryEnterCriticalSection從來不掛起線程。

 

Mutex:(互斥對象包含一個使用數(shù)量,一個線程ID和一個引用計數(shù)器)

當兩個或更多線程需要同時訪問一個共享資源時,系統(tǒng)需要使用同步機制來確保一次只有一個線程使用該資源。Mutex只向一個線程授予對共享資源的獨占訪問權。 如果一個線程獲取了互斥體,則要獲取該互斥體的第二個線程將被掛起,直到第一個線程釋放該互斥體。

互斥量對象與所有其它內(nèi)核對象的不同之處在于它是被線程所擁有的。其它所有同步對象要么有信號,要么無信號,僅此而已。而互斥量對象除了記錄當前信號狀態(tài)外,還要記住此時那個線程擁有它。如果一個線程在得到一個互斥量對象 (即將其置為無信號態(tài))后就終結了,互斥量也就廢棄了。在這種情況下,互斥量將永遠保持無信號態(tài),因為沒有其它線程能夠通過調用ReleaseMutex來釋放它。系統(tǒng)發(fā)現(xiàn)產(chǎn)生這種情況時,就自動將互斥量設回有信號狀態(tài)。(將線程ID置為零,引用計數(shù)置零)其它等待該信號量的線程就會被喚醒,但函數(shù)的返回值為WAIT_ABANDONED而不是正常的WAIT_OBJECT_0。這時,其它線程可以通過等待的返回值知道互斥量是不是被正常釋放。

  互斥量與CRITICAL_SECTION類似。擁有該互斥量的線程,每次調用WaitForSingleObject都會立即成功返回,但互斥量的使用計數(shù)將增加,同樣的,也要多次調用ReleaseMutex以使引用計數(shù)變?yōu)榱?,方可供別的線程使用。

問:其它內(nèi)核對象在線程異常終止沒有釋放所有權時,系統(tǒng)回重置其狀態(tài)嗎?

答:重置,但沒有任何標記,與正常釋放無異,即不會擁有互斥量的這個返回WAIT_ABANDONED的特性。

注意:線程擁有某個內(nèi)核對象和線程擁有某個內(nèi)核對象的所有權,這二者是不同的。當說線程擁有某個內(nèi)核對象時,強調的是當該線程終止時,若線程正好擁有該內(nèi)核對象的訪問權,內(nèi)核對象也將被廢棄,因為不能重置其信號狀態(tài);而線程擁有某一個內(nèi)核對象的所用權,指的是線程可以調用某些函數(shù),訪問該內(nèi)核對象或對該內(nèi)核對象執(zhí)行某些操作。 

以互斥內(nèi)核對象來保持線程同步可能用到的函數(shù)主要有CreateMutex()、OpenMutex()、ReleaseMutex()、 WaitForSingleObject()和WaitForMultipleObjects()等。在使用互斥對象前,首先要通過 CreateMutex()或OpenMutex()創(chuàng)建或打開一個互斥對象。CreateMutex()函數(shù)原型為:

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

 參數(shù)bInitialOwner主要用來控制互斥對象的初始狀態(tài)。一般多將其設置為FALSE,以表明互斥對象在創(chuàng)建時并沒有為任何線程所占有。如果在創(chuàng)建互斥對象時指定了對象名,那么可以在本進程其他地方或是在其他進程通過OpenMutex()函數(shù)得到此互斥對象的句柄。 OpenMutex()函數(shù)原型為:
HANDLE OpenMutex
(
 DWORD dwDesiredAccess, // 訪問標志
 BOOL bInheritHandle, // 繼承標志
 LPCTSTR lpName // 互斥對象名
);
        當目前對資源具有訪問權的線程不再需要訪問此資源而要離開時,必須通過ReleaseMutex()函數(shù)來釋放其擁有的互斥對
象,其函數(shù)原型為:BOOL ReleaseMutex(HANDLE hMutex),其唯一的參數(shù)hMutex為待釋放的互斥對象句柄。
       但是這里需要特別指出的是:在互斥對象通知引起調用等待函數(shù)返回時,等待函數(shù)的返回值不再是通常的WAIT_OBJECT_0(對于 WaitForSingleObject()函數(shù))或是在WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1之間的一個值(對于 WaitForMultipleObjects()函數(shù)),而是將返回一個WAIT_ABANDONED_0(對于 WaitForSingleObject()函數(shù))或是在WAIT_ABANDONED_0到WAIT_ABANDONED_0+nCount-1之間的一個值(對于WaitForMultipleObjects()函數(shù))。以此來表明線程正在等待的互斥對象由另外一個線程所擁有,而此線程卻在使用完共享資源前就已經(jīng)終止。      、

 

Event:(分為自動重置事件和人工重置事件)

事件對象也可以通過通知操作的方式來保持線程的同步。主要函數(shù)有: CreateEvent(),OpenEvent(),SetEvent(),ResetEvent(),WaitForSingleObject()和WaitForMultipleObjects()等。
        使用臨界區(qū)只能同步同一進程中的線程,而使用事件內(nèi)核對象則可以對進程外的線程進行同步,其前提是得到對此事件對象的訪問權。可以通過OpenEvent()函數(shù)獲取得到,其函數(shù)原型為:
HANDLE OpenEvent(
 DWORD dwDesiredAccess, // 訪問標志
 BOOL bInheritHandle, // 繼承標志
 LPCTSTR lpName // 指向事件對象名的指針
);
        如果事件對象已創(chuàng)建,函數(shù)將返回指定事件的句柄。對于那些在創(chuàng)建事件時沒有指定事件名的事件內(nèi)核對象,可以通過使用內(nèi)核對象的繼承性或是調用DuplicateHandle()函數(shù)來調用CreateEvent()以獲得對指定事件對象的訪問權。在獲取到訪問權后所進行的同步操作與在同一個進程中所進行的線程同步操作是一樣的。
        如果需要在一個線程中等待多個事件,則用WaitForMultipleObjects()來等待。 WaitForMultipleObjects()與WaitForSingleObject()類似,同時監(jiān)視位于句柄數(shù)組中的所有句柄。這些被監(jiān)視對象的句柄享有平等的優(yōu)先權,任何一個句柄都不可能比其他句柄具有更高的優(yōu)先權。WaitForMultipleObjects()的函數(shù)原型為:
DWORD WaitForMultipleObjects(
    DWORD nCount, // 等待句柄數(shù)
 CONST HANDLE *lpHandles, // 句柄數(shù)組首地址
 BOOL fWaitAll, // 等待標志
 DWORD dwMilliseconds // 等待時間間隔
);
        參數(shù)nCount指定了要等待的內(nèi)核對象的數(shù)目,存放這些內(nèi)核對象的數(shù)組由lpHandles來指向。fWaitAll對指定的這nCount 個內(nèi)核對象的兩種等待方式進行了指定,為TRUE時當所有對象都被通知時函數(shù)才會返回,為FALSE則只要其中任何一個得到通知就可以返回。 dwMilliseconds在這里的作用與在WaitForSingleObject()中的作用是完全一致的。如果等待超時,函數(shù)將返回 WAIT_TIMEOUT。如果返回WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1中的某個值,則說明所有指定對象的狀態(tài)均為已通知狀態(tài)(當fWaitAll為TRUE時)或是用返回值減去WAIT_OBJECT_0可得到發(fā)生通知的對象的索引(當fWaitAll為FALSE 時)。如果返回值在WAIT_ABANDONED_0與WAIT_ABANDONED_0+nCount-1之間,則表示所有指定對象的狀態(tài)均為已通知,且其中至少有一個對象是被丟棄的互斥對象(當fWaitAll為TRUE時),或是用返回值減去WAIT_OBJECT_0可得到表示一個等待正常結束的互斥對象的索引(當fWaitAll為FALSE時)。

 

Semaphores:(允許多個線程同時訪問一個資源)

 信號量對象對線程的同步方式與前面幾種方法不同,信號允許多個線程同時使用共享資源。它允許多個線程在同一時刻訪問同一資源,但是需要限制在同一時刻訪問此資源的最大線程數(shù)目。在用CreateSemaphore()創(chuàng)建信號量時即要同時指出允許的最大資源計數(shù)和當前可用資源計數(shù)。一般是將當前可用資源計數(shù)設置為最大資源計數(shù),每增加一個線程對共享資源的訪問,當前可用資源計數(shù)就會減1,只要當前可用資源計數(shù)是大于0的,就可以發(fā)出信號量信號。但是當前可用計數(shù)減小到0時則說明當前占用資源的線程數(shù)已經(jīng)達到了所允許的最大數(shù)目,不能在允許其他線程的進入,此時的信號量信號將無法發(fā)出。線程在處理完共享資源后,應在離開的同時通過ReleaseSemaphore()函數(shù)將當前可用資源計數(shù)加1。在任何時候當前可用資源計數(shù)決不可能大于最大資源計數(shù)。 信號量是通過計數(shù)來對線程訪問資源進行控制的,而實際上信號量確實也被稱作Dijkstra計數(shù)器。
        信號量內(nèi)核對象進行線程同步主要會用到CreateSemaphore()、OpenSemaphore()、 ReleaseSemaphore()、WaitForSingleObject()和WaitForMultipleObjects()等函數(shù)。

 CreateSemaphore()用來創(chuàng)建一個信號量內(nèi)核對象,其函數(shù)原型為:
HANDLE CreateSemaphore(
 LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // 安全屬性指針
 LONG lInitialCount, // 初始計數(shù)
 LONG lMaximumCount, // 最大計數(shù)
 LPCTSTR lpName // 對象名指針
);
        參數(shù)lMaximumCount是一個有符號32位值,定義了允許的最大資源計數(shù),最大取值不能超過4294967295。lpName參數(shù)可以為創(chuàng)建的信號量定義一個名字,由于其創(chuàng)建的是一個內(nèi)核對象,因此在其他進程中可以通過該名字而得到此信號量。

 OpenSemaphore()函數(shù)即可用來根據(jù)信號量名打開在其他進程中創(chuàng)建的信號量,函數(shù)原型如下:
HANDLE OpenSemaphore(
 DWORD dwDesiredAccess, // 訪問標志
 BOOL bInheritHandle, // 繼承標志
 LPCTSTR lpName // 信號量名
);
        在線程離開對共享資源的處理時,必須通過ReleaseSemaphore()來增加當前可用資源計數(shù)。否則將會出現(xiàn)當前正在處理共享資源的實際線程數(shù)并沒有達到要限制的數(shù)值,而其他線程卻因為當前可用資源計數(shù)為0而仍無法進入的情況。        
        ReleaseSemaphore()的函數(shù)原型為:
BOOL ReleaseSemaphore(
 HANDLE hSemaphore, // 信號量句柄
 LONG lReleaseCount, // 計數(shù)遞增數(shù)量
 LPLONG lpPreviousCount // 先前計數(shù)
);
        該函數(shù)將lReleaseCount中的值添加給信號量的當前資源計數(shù),一般將lReleaseCount設置為1,如果需要也可以設置其他的值。

 信號量的使用特點使其更適用于對Socket(套接字)程序中線程的同步。例如,網(wǎng)絡上的HTTP服務器要對同一時間內(nèi)訪問同一頁面的用戶數(shù)加以限制,這時可以為沒一個用戶對服務器的頁面請求設置一個線程,而頁面則是待保護的共享資源,通過使用信號量對線程的同步作用可以確保在任一時刻無論有多少用戶對某一頁面進行訪問,只有不大于設定的最大用戶數(shù)目的線程能夠進行訪問,而其他的訪問企圖則被掛起,只有在有用戶退出對此頁面的訪問后才有可能進入。

總結: 

互斥量與臨界區(qū)的作用非常相似,但互斥量是可以命名的,也就是說它可以跨越進程使用。所以創(chuàng)建互斥量需要的資源更多,所以如果只為了在進程內(nèi)部是用的話使用臨界區(qū)會帶來速度上的優(yōu)勢并能夠減少資源占用量。因為互斥量是跨進程的互斥量一旦被創(chuàng)建,就可以通過名字打開它。

互斥量(Mutex),信號燈(Semaphore),事件(Event)都可以被跨越進程使用來進行同步數(shù)據(jù)操作,而其他的對象與數(shù)據(jù)同步操作無關,但對于進程和線程來講,如果進程和線程在運行狀態(tài)則為無信號狀態(tài),在退出后為有信號狀態(tài)。所以可以使用WaitForSingleObject來等待進程和線程退出。

WaitForSingleObject,在一個指定時間(dwMilliseconds)內(nèi)等待某一個內(nèi)核對象變?yōu)橛行盘?,在此時間內(nèi),若等待的內(nèi)核對象一直是無信號的,則調用線程將睡眠,否則繼續(xù)執(zhí)行。超過此時間后,線程繼續(xù)運行。函數(shù)返回值可能為:WAIT_OBJECT_0、WAIT_TIMEOUT、WAIT_ABANDONED(僅當內(nèi)核對象為互斥量時)、WAIT_FAILED。

WaitForMultipleObjects與WaitForSingleObject類似,只是它要么等待指定列表(由lpHandles指定)中若干個對象(由nCount決定)都變?yōu)橛行盘?,要么等待一個列表(由lpHandles指定)中的某一個對象變?yōu)橛行盘枺ㄓ蒪WaitAll決定)。

WaitForSingleObject和WaitForMultipleObjects函數(shù)對特定的內(nèi)核對象有重要的副作用,即它們根據(jù)不同的內(nèi)核對象,會決定是否改變內(nèi)核對象的信號狀態(tài),并執(zhí)行這種改變;這些副作用,決定了是讓等待該內(nèi)核對象的進程或線程中的某一個被喚醒還是全都被喚醒。

(1) 對進程和線程內(nèi)核對象,這兩個函數(shù)不產(chǎn)生副作用。

      在進程或線程內(nèi)核對象變?yōu)橛行盘柡?,它們將保持有信號,這兩個函數(shù)不會試圖改變內(nèi)核對象的信號狀態(tài)。這樣,所有等待這些內(nèi)核對象的線程都會被喚醒。

(2) 對于互斥量、自動重置事件和自動重置可等的計時器對象,這兩個函數(shù)將把它們的狀態(tài)改為無信號。

      換言之一旦這些對象變?yōu)橛行盘柌⑶矣幸粋€線程被喚醒,則對象重被置為無信號狀態(tài)。于是,只有一個正在等待的線程醒來,其它等待的線程將繼續(xù)睡眠。

(3) 對于WaitForMultipleObjects函數(shù)還有非常重要的一個特性:當調用它時傳遞的bWaitAll為TRUE時,在所有被等待的對象都變?yōu)橛行盘栔埃坏却娜魏慰梢员桓淖儬顟B(tài)的內(nèi)核對象都不被重置為無信號狀態(tài)。換言之,在傳入?yún)?shù)bWaitAll為TRUE,WaitForMultipleObjects除非能取得所有指定對象(由lpHandles指定)的所有權,它不會取得單個對象的所有權(不能取得所有權,自然也不會改變此對象的信號狀態(tài))。這是為了防止死鎖。換言之,在bWaitAll為TRUE時,WaitForMultipleObjects不會在沒有獲得所有被等對象所有權的情形下改變某一可以被改變狀態(tài)的內(nèi)核對象的信號狀態(tài),任何以同樣方式等待的線程都不會被喚醒,但以其它方式等待的線程將被喚醒。

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

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多