每個進程中訪問臨界資源的那段代碼稱為臨界區(Critical Section)(臨界資源是一次僅允許一個進程使用的共享資源)。
每次只准許一個進程進入臨界區,進入后不允許其他進程進入。不論是硬件臨界資源,還是軟件臨界資源,多個進程必須互斥地對它進行訪問。
如果有多個線程試圖同時訪問臨界區,那么在有一個線程進入后其他所有試圖訪問此臨界區的線程將被掛起,並一直持續到進入臨界區的線程離開。臨界區在被釋放后,其他線程可以繼續搶占,並以此達到用原子方式操作共享資源的目的。 臨界區在使用時以CRITICAL_SECTION結構對象保護共享資源,並分別用EnterCriticalSection()和LeaveCriticalSection()函數去標識和釋放一個臨界區。所用到的CRITICAL_SECTION結構對象必須經過InitializeCriticalSection()的初始化后才能使用,而且必須確保所有線程中的任何試圖訪問此共享資源的代碼都處在此臨界區的保護之下。否則臨界區將不會起到應有的作用,共享資源依然有被破壞的可能。
在使用臨界區時,一般不允許其運行時間過長,只要進入臨界區的線程還沒有離開,其他所有試圖進入此臨界區的線程都會被掛起而進入到等待狀態,並會在一定程度上影響程序的運行性能。尤其需要注意的是不要將等待用戶輸入或是其他一些外界干預的操作包含到臨界區。如果進入了臨界區卻一直沒有釋放,同樣也會引起其他線程的長時間等待。換句話說,在執行了EnterCriticalSection()語句進入臨界區后無論發生什么,必須確保與之匹配的LeaveCriticalSection()都能夠被執行到。可以通過添加結構化異常處理代碼來確保LeaveCriticalSection()語句的執行。雖然臨界區同步速度很快,但卻只能用來同步本進程內的線程,而不可用來同步多個進程中的線程。
任一時刻只有一個線程可以擁有臨界區對象,擁有臨界區的線程可以訪問被保護起來的資源或代碼段,其他希望進入臨界區的線程將被掛起等待,直到擁有臨界區的線程放棄臨界區時為止,這樣就保證了不會在同一時刻出現多個線程訪問共享資源。
#include <windows.h>
#include <process.h>
int arr[10];
UINT __stdcall Add(LPVOID lParam)
{
EnterCriticalSection(&g_data);
for (int i = 0; i<10;i++ )
{
arr[i]=i;//0-9
}
LeaveCriticalSection(&g_data);
return 1;
}
UINT __stdcall Add2(LPVOID lParam)
{
EnterCriticalSection(&g_data);
for (int i = 9; i>=0 ;i--)
{
arr [i] = 10-i;//10`1
}
LeaveCriticalSection(&g_data);
return 1;
}
int main()
{
InitializeCriticalSection(&g_data); //初始化臨界區(關鍵代碼段)
//MFC使用更簡單的CCriticalSection lock加鎖,unlock解鎖。原理相同
//#include <afxmt.h>
hUp=(HANDLE)_beginthreadex(NULL, 0, Add, NULL, NULL, 0);
hUp=(HANDLE)_beginthreadex(NULL, 0, Add2, NULL, NULL, 0);
//AfxBeginThread(Add, NULL);
Sleep(1000);//注意Sleep在輸出之前,否則可能先執行輸出的一部分,再執行線程
for (int i = 0;i < 10; i++)
{
cout<<arr[i]<<" ";
}
DeleteCriticalSection(&g_data); //刪除臨界區(關鍵代碼段)
}