在使用多線程時,一般很少有多個線程完全獨立的工作。往往是多個線程同時操作一個全局變量來獲取程序的運行結果。多個線程同時訪問同一個全局變量,如果都是讀取操作,則不會出現問題。如果是寫操作,則會發生錯誤。這時候,我們可以通過臨界區,爲全局變量設置一個保護,保證同時只有一個線程可以訪問此變量,其他變量進入等待狀態。
臨界區(Critical Section)是一段獨佔對某些共享資源訪問的代碼,在任意時刻只允許一個線程對共享資源進行訪問。如果有多個線程試圖同時訪問臨界區,那麼在有一個線程進入後其他所有試圖訪問此臨界區的線程將被掛起,並一直持續到進入臨界區的線程離開。臨界區在被釋放後,其他線程可以繼續搶佔,並以此達到用原子方式操作共享資源的目的。
臨界區在使用時以CRITICAL_SECTION結構對象保護共享資源,並分別用 ①InitializeCriticalSection(&cs); //初始化臨界區②EnterCriticalSection(&cs);//進入臨界區③LeaveCriticalSection(&cs);//離開臨界區④DeleteCriticalSection(&cs);//刪除臨界區
一般類程序(舉例)
CRITICAL_SECTION g_cs;
// 共享資源
char g_cArray[10]
UINT ThreadProc1(LPVOID pParam)
{
// 進入臨界區
EnterCriticalSection(&g_cs);
// 對共享資源進行寫入操作
for (int i = 0; i < 10; i++)
{
g_cArray[i] = ’a’;
Sleep(1);
}
// 離開臨界區
LeaveCriticalSection(&g_cs);
return 0;
}
UINT ThreadProc2(LPVOID pParam)
{
// 進入臨界區
EnterCriticalSection(&g_cs);
// 對共享資源進行寫入操作
for (int i = 0; i < 10; i++)
{
g_cArray[10 - i - 1] = ’b’;
Sleep(1);
}
// 離開臨界區
LeaveCriticalSection(&g_cs);
return 0;
}
void CSampleView::OnCriticalSection()
{
// 初始化臨界區
InitializeCriticalSection(&g_cs);
// 啓動線程
AfxBeginThread(ThreadProc1, NULL);
AfxBeginThread(ThreadProc2, NULL);
// 等待計算完畢
Sleep(300);
// 報告計算結果
CString sResult = CString(g_cArray);
AfxMessageBox(sResult);
}
在使用臨界區時,一般不允許其運行時間過長,只要進入臨界區的線程還沒有離開,其他所有試圖進入此臨界區的線程都會被掛起而進入到等待狀態,並會在一定程度上影響。程序的運行性能。尤其需要注意的是不要將等待用戶輸入或是其他一些外界干預的操作包含到臨界區。如果進入了臨界區卻一直沒有釋放,同樣也會引起其他線程的長時間等待。換句話說,在執行了EnterCriticalSection()語句進入臨界區後無論發生什麼,必須確保與之匹配的LeaveCriticalSection()都能夠被執行到。可以通過添加結構化異常處理代碼來確保LeaveCriticalSection ()語句的執行。雖然臨界區同步速度很快,但卻只能用來同步本進程內的線程,而不可用來同步多個進程中的線程。
MFC爲臨界區提供有一個CCriticalSection類,使用該類進行線程同步處理是非常簡單的,只需在線程函數中用CCriticalSection類成員函數 Lock()和UnLock()標定出被保護代碼片段即可。對於上述代碼,可通過CCriticalSection類將其改寫如下:
MFC應用(舉例)
CCriticalSection g_cs;
// 共享資源
char g_cArray[10];
UINT ThreadProc1(LPVOID pParam)
{
// 進入臨界區
g_cs.Lock();
// 對共享資源進行寫入操作
for (int i = 0; i < 10; i++)
{
g_cArray[i] = ’a’;
Sleep(1);
}
// 離開臨界區
g_cs.Unlock();
return 0;
}
UINT ThreadProc2(LPVOID pParam)
{
// 進入臨界區
g_cs.Lock();
// 對共享資源進行寫入操作
for (int i = 0; i < 10; i++)
{
g_cArray[10 - i - 1] = ’b’;
Sleep(1);
}
// 離開臨界區
g_cs.Unlock();
return 0;
}
void CSampleView::OnCriticalSectionMfc()
{
// 啓動線程
AfxBeginThread(ThreadProc1, NULL);
AfxBeginThread(ThreadProc2, NULL);
// 等待計算完畢
Sleep(300);
// 報告計算結果
CString sResult = CString(g_cArray);
AfxMessageBox(sResult);
}