臨界區

臨界區

每個進程中訪問臨界資源的那段代碼
每個進程中訪問臨界資源的那段代碼稱為臨界區。顯然,若能保證諸進程互斥地進入自己的臨界區,便可實現諸進程對臨界資源的互斥訪問。為此,每個進程在進入臨界區之前,應先對欲訪問的臨界資源進行檢查,看它是否正被訪問。如果此刻該臨界資源未被訪問,進程便可進入臨界區對該資源進行訪問,并設置它正被訪問的标志;如果此刻該臨界資源正被某進程訪問,則本進程不能進入臨界區。[1]
  • 中文名:臨界區
  • 外文名:Critical section
  • 别名:

簡介

每個進程中訪問臨界資源的那段代碼稱為臨界區(Critical Section)、(臨界資源是一次僅允許一個進程使用的共享資源)。每次隻準許一個進程進入臨界區,進入後不允許其他進程進入。不論是硬件臨界資源,還是軟件臨界資源,多個進程必須互斥地對它進行訪問。

多個進程中涉及到同一個臨界資源的臨界區稱為相關臨界區。

程序調度法則

進程進入臨界區的調度原則是:

1、如果有若幹進程要求進入空閑的臨界區,一次僅允許一個進程進入。

2、任何時候,處于臨界區内的進程不可多于一個。如已有進程進入自己的臨界區,則其它所有試圖進入臨界區的進程必須等待。

3、進入臨界區的進程要在有限時間内退出,以便其它進程能及時進入自己的臨界區。

4、如果進程不能進入自己的臨界區,則應讓出CPU,避免進程出現“忙等”現象。

線程同步問題

有多個線程試圖同時訪問臨界區,那麼在有一個線程進入後其他所有試圖訪問此臨界區的線程将被挂起,并一直持續到進入臨界區的線程離開。臨界區在被釋放後,其他線程可以繼續搶占,并以此達到用原子方式操作共享資源的目的。

臨界區在使用時以CRITICAL_SECTION結構對象保護共享資源,并分别用EnterCriticalSection()和LeaveCriticalSection()函數去标識和釋放一個臨界區。所用到的CRITICAL_SECTION結構對象必須經過InitializeCriticalSection()的初始化後才能使用,而且必須确保所有線程中的任何試圖訪問此共享資源的代碼都處在此臨界區的保護之下。否則臨界區将不會起到應有的作用,共享資源依然有被破壞的可能。

下面通過一段代碼展示了臨界區在保護多線程訪問的共享資源中的作用。通過兩個線程來分别對全局變量g_cArray進行寫入操作,用臨界區結構對象g_cs來保持線程的同步,并在開啟線程前對其進行初始化。為了使實驗效果更加明顯,體現出臨界區的作用,在線程函數對共享資源g_cArray的寫入時,以Sleep()函數延遲1毫秒,使其他線程同其搶占CPU的可能性增大。如果不使用臨界區對其進行保護,則共享資源數據将被破壞(參見圖1(a)所示計算結果),而使用臨界區對線程保持同步後則可以得到正确的結果(參見圖1(b)所示計算結果)。

代碼實現清單附下:

臨界區存在的幾個問題

在使用臨界區時,一般不允許其運行時間過長,隻要進入臨界區的線程還沒有離開,其他所有試圖進入此臨界區的線程都會被挂起而進入到等待狀态,并會在一定程度上影響程序的運行性能。尤其需要注意的是不要将等待用戶輸入或是其他一些外界幹預的操作包含到臨界區。如果進入了臨界區卻一直沒有釋放,同樣也會引起其他線程的長時間等待。換句話說,在執行了EnterCriticalSection()語句進入臨界區後無論發生什麼,必須确保與之匹配的LeaveCriticalSection()都能夠被執行到。可以通過添加結構化異常處理代碼來确保LeaveCriticalSection()語句的執行。雖然臨界區同步速度很快,但卻隻能用來同步本進程内的線程,而不可用來同步多個進程中的線程。

1、 臨界區的退出,不會檢測是否是已經進入的線程,也就是說,我可以在A線程中調用進入臨界區函數,在B線程調用退出臨界區的函數,同樣是成功;

2、 我在測試臨界區的時候,如果我沒有調用進入臨界區的函數,直接退出的話,系統沒有進行判斷,但是計數發現了改變,此時此臨界區就再也用不了了,因為結構中的數據已經亂掉了。

解決方法如下:

ypedef class mutex_lock

{

public:

mutex_lock()

: LockCount(-1)

, hEvent(0)

{

}

~mutex_lock()

{

if(NULL != this->hEvent)

{

CloseHandle(this->hEvent);

}

this->hEvent = NULL;

this->LockCount = -1;

}

long GetLock();

long ReleaseLock();

private:

mutex_lock(mutex_lock&);

long LockCount;

HANDLE hEvent;

} MUTEXLOCK, *LPMUTEXLOCK;

long mutex_lock::GetLock()

{

__asm

{

movebx, [this];//把基址保存在ebx中

lock incdword ptr [ebx];//對LockCount進行加鎖加,保證多CPU時的唯一性,

jeLRET1;//如果LockCount加1為0的話,表示沒有人在使用資源,同時利用上面的互斥加對資源進行占用。

cmpdword ptr [ebx+4], 0;//此時LockCount加1大于0的情況,表示已有人使用此資源,要對此資源進行加内核鎖

jneL1;//如果平常沒有創建内核鎖,則進行創始一個

push 0;

push 0;

push 0;

push 0;

call dword ptr [CreateEvent];

movedx, eax;//創建内存鎖成功後,在同樣對hEvent變量進行互斥比較後替換,以處理多個線程同時對此值進行替換的情況,保證隻有一個能夠成功替換

moveax, 0;

lock cmpxchg dword ptr [ebx+4], edx; //互斥比較替換

jeL1;

push edx;//如果已經被别人替換過了,需要把自己創建的内核鎖釋放

call dword ptr [CloseHandle];

L1:

push INFINITE;

push [ebx+4];

call dword ptr [WaitForSingleObject]; //在内核級進行等待

LRET1:

moveax, 0

}

}

long mutex_lock::ReleaseLock()

{

__asm

{

movebx, [this];//把基址保存在ebx中

moveax, -1;

lock xadd dword ptr [ebx], eax; //進行交換自減交換操作,運行後,eax中是第一操作數先前的值,此值用會返回用來判斷是否多調用了ReleaseLock

jlLRET2;//沒有别的線程占用資源,直接返回,用戶可以通過返回值,分析是否失敗

cmpdword ptr [ebx+4], 0;//有别的線程在等待,檢查内核鎖,如果沒有則進行創建

jneL2;

push 0;

push 0;

push 0;

push 0;

call dword ptr [CreateEvent];

movedx, eax;

moveax, 0;

lock cmpxchg dword ptr [ebx+4], edx; //互斥替換内核鎖句柄

jeL2;//已經有内核鎖了,把自已申請的關閉

push edx;

call dword ptr [CloseHandle];

L2:

push [ebx+4];//設計信号,喚醒一個線程

call dword ptr [SetEvent];

moveax, 0;

LRET2:

}

}

上一篇:歐洲美元期貨

下一篇:北京蓮花池客運站

相關詞條

相關搜索

其它詞條