LPCRITICAL_SECTION 函數


終於找到整個工程的關鍵部分了:就是

 

在C++里,線程同步控制主要有四種方式:

1.Critial Sections 臨界區域
2.mutex 互斥
3.Semaphone 信號量
4.事件

一.
   所謂臨界區,就是指一塊處理共享資源的代碼。這段代碼可以用Critial Sections保護起來,確保一次只有一個線程進入該代碼
即進入臨界區。
   Critial Sections並不是核心對像,沒有Handle,也沒有像創建核心對像一樣Create函數,只需要初始化一個CRITICAL_SECTION類型
的變量。所以Critial Sections相對其他三種同步方法比較簡單,但同時也失去了靈活性。
   
  對於Critial Sections,WINDOW API提供了四個函數:
    
     1. VOID InitializeCritialSection(LPCRITICAL_SECTION lpCs);     --初如化變量
     2. VOID DeleteCritialSection(LPCRITICAL_SECTION lpCs)          ---清除變量
     3. VOID EnterCritialSection(LPCRITICAL_SECTION lpCs)           --進入臨界區即加鎖 
     4. VOID LeaveCritialSection(LPCRITICAL_SECTION lpCs)           --離開臨界區即解鎖.

下面是一個簡單的模擬售票的程序。

 

View Code
 1 #include "stdafx.h"
 2 #include <windows.h>
 3 
 4 LONG g_value = 49;
 5 CRITICAL_SECTION cs;
 6 
 7 DWORD WINAPI ThreadFun(LPVOID lpParam){
 8  for (int i=0;i<50;i++)
 9  {
10   //EnterCriticalSection(&cs);
11   if (g_value > 0)
12   {
13    Sleep(1);
14    printf("售出第%d張\n",49-g_value+1);
15          g_value--; 
16   }
17   //LeaveCriticalSection(&cs);
18  }
19  return 0;
20 }
21 
22 int _tmain(int argc, _TCHAR* argv[])
23 {
24  InitializeCriticalSection(&cs);
25  HANDLE handles[2];
26  for (int i=0;i<2;i++)
27  {
28   handles[i] = CreateThread(NULL,0,ThreadFun,NULL,0,NULL);
29  }
30  Sleep(100000);//等待以致兩個子線程運行完成。
31 
32  for (int i=0;i<2;i++)
33  {
34   CloseHandle(handles[i]);
35  }
36  DeleteCriticalSection(&cs);
37  return 0;
38 }
39 

 

 為了讓效果更明顯,線程函數里加Sleep(1),讓其他線程得以執行。

售出第1張
售出第1張
售出第3張
售出第4張
售出第5張
售出第6張
售出第7張
售出第8張
售出第9張
售出第9張
售出第11張
售出第12張
售出第13張
售出第14張
售出第15張
售出第16張
售出第17張
售出第18張
售出第19張
售出第20張
售出第21張
售出第21張
售出第23張
售出第24張
售出第25張
售出第26張
售出第27張
售出第28張
售出第29張
售出第30張
售出第31張
售出第32張
售出第33張
售出第33張
售出第35張
售出第36張
售出第37張
售出第38張
售出第39張
售出第40張
售出第41張
售出第42張
售出第43張
售出第44張
售出第45張
售出第45張
售出第47張
售出第48張
售出第49張
售出第50張

可以看出來,這些數據是亂的,甚至多出一張票。當線程1執行到第二行時,判斷G_value>0成立,這里sleep,線程2執行,將G_value減為了0.但線程1下一個運行時刻,G_value已經是0。所以才會買到50張票。把10與此17行的注釋打開,一切正常。

看到有些網友對CRITICAL_SECTION 理解存在一些誤區,認為CRITICAL_SECTION 對g_value進行了鎖定.其實不然,如果在其他非臨界區內,g_value同樣可非同步訪問了.所以說CRITICAL_SECTION 鎖定的是代碼塊,而不是具體變量.

我個人認為Java中synchronized與CRITICAL_SECTION 比較相近,我們可以認為

 1. VOID InitializeCritialSection(LPCRITICAL_SECTION lpCs);     --為變量Cs關聯了一把鎖

 2. VOID DeleteCritialSection(LPCRITICAL_SECTION lpCs)          ---清除變量Cs關聯的鎖 

3. VOID EnterCritialSection(LPCRITICAL_SECTION lpCs)           --嘗試獲取變量Cs上鎖.獲得則進入代碼塊,否則等待. 
 4. VOID LeaveCritialSection(LPCRITICAL_SECTION lpCs)           --釋放獲得的CS上的鎖.

暫不知妥否,請指正.

 二.mutrex

Critial Section簡單易用。但是正如上面所說,這是以其靈活性對代價的。看下面這個線程函數:
void Swap(Object* first,Objct* second){
 EnterCriticalSection(&first->cs);  
 EnterCriticalSection(&second->cs);
//do swap  
 LeaveCriticalSection(&second->cs);
 LeaveCriticalSection(&first->cs);
}

假如兩個線程幾乎在同一時間內調用Swap函數。

void Swap(first,second)
void Swap (second,first)

當線程1進入第一個Critical Section時,線程發生調度。然后線程2也進入第一個Critical Section。這時就發生死鎖。
這是我們想到用WaitForMultiObject函數,要么全獲得,要么一個都不要。但又發現Critical Section沒有Handle.
無處入手。這時我們可以采用mutrex 取而代之。

下面是一個簡單的對比表:
 
   Critical_Section                                           Mutrex

   鎖住Critical_Section的時間比鎖住                     Mutrex有Handle,可以有名字。
   Mutrex快很多。Critical_Section在用戶狀執行       Mutrex可以跨進程。
   Mutrex在核心態。
    
   InitializeCritialSection                                  CreateMutrex
                                                                   OpenMutrex

   EnterCritialSection                                  
                                                                    WaitForSingleObject
                                                                    WaitForMultipleObject
                                                                    MsgWaitForMultipleObject
  LeaveCritialSection                                        ReleaseMutrex
  DeleteCritialSection                                       CloseHandle.

WIDOW API對Mutrex的操作主要有以下幾個函數
 1.Handle CreateMutrex(LPSECUEIRY_ATTRIBUTES,BOOL initOwner,LPSTR Name); --創建一個互斥量,如果讓當前線程鎖住該Mutrex。 
   initOwner為TRUE,name設定Mutrex的名字。
 2.HANDLE OpenMutex(DWORD dwDesiredAccess, BOOL bInheritHandle,LPCTSTR lpName); --打開一個Mutex
 3.WaitForSingleObject,WaitForMultipleObject,MsgWaitForMultipleObject 鎖定mutrex
 4.ReleaseMutex(Handle mutrex) 釋放Mutex,線程結束同樣會釋放Mutex。
 
  上面的例子換成Mutex,消除了死鎖的可能性:

 void Swap(Object* first,Objct* second){
 HANDLE hs[2] = HANDLE[]{first->mutrex,second->mutrex};   
 MsgWaitForMultipleObject(2,hs,true,INFINITE);
  //do swap 
 ReleaseMutex(hs[0]);
 ReleaseMutex(hs[1]);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM